diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..f6b8fdf --- /dev/null +++ b/.clang-format @@ -0,0 +1,2 @@ +--- +BasedOnStyle: LLVM diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 173386d..50cfa0d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,7 +1,6 @@ name: CI on: - push: pull_request: jobs: @@ -24,13 +23,15 @@ jobs: - name: Run clang-format style check uses: jidicula/clang-format-action@v4.14.0 with: - clang-format-version: '19' + clang-format-version: '20' check-path: '.' - build: + test: needs: formatting-check - name: Build + name: Test runs-on: ubuntu-22.04 + container: + image: ghcr.io/egorshamshura/protea-build:latest strategy: fail-fast: false matrix: @@ -44,11 +45,8 @@ jobs: - name: Checkout repository uses: actions/checkout@v4 - - name: Set up Ruby - uses: ruby/setup-ruby@v1 - with: - ruby-version: '3.4.8' - bundler-cache: true - - name: Build run: ci-extra/build.sh "$CMAKE_PRESET" + + - name: Run tests + run: ci-extra/test.sh "$CMAKE_PRESET" diff --git a/CMakeLists.txt b/CMakeLists.txt index 85689ce..826e46f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,19 +2,34 @@ cmake_minimum_required(VERSION 3.22 FATAL_ERROR) project(protea) +set(TARGET_NAME RISCV) + +option(PROTEA_BUILD_TESTS + "Enable tests build (requires riscv gnu toolchain)" OFF) if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) set_property(CACHE CMAKE_INSTALL_PREFIX PROPERTY VALUE "${CMAKE_BINARY_DIR}/install") endif() -find_package(Ruby 3.4.8 EXACT REQUIRED) +find_package(Ruby 4.0.1 EXACT REQUIRED) +find_package(Python3 3.10 EXACT REQUIRED COMPONENTS Interpreter) +message("Python found: ${Python3_EXECUTABLE}") +include(ExternalProject) include(cmake/Bundler.cmake) +include(cmake/QEMU.cmake) include(cmake/CompilerOptions.cmake) include(cmake/CPM.cmake) include(cmake/dependencies.cmake) +include(cmake/CompilerConfig.cmake) + +if(PROTEA_BUILD_TESTS) + enable_testing() +endif() add_subdirectory(lib) add_subdirectory(sim_lib) add_subdirectory(sim_gen) +add_subdirectory(sim_gen_lira) +add_subdirectory(test) diff --git a/CMakePresets.json b/CMakePresets.json index d6d1a3f..0d67ae2 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -8,13 +8,11 @@ "configurePresets": [ { "name": "Base", - "description": "General preset that applies to all configurations", "hidden": true, "binaryDir": "${sourceDir}/build/${presetName}" }, { "name": "Default-Release", - "description": "Release build", "inherits": "Base", "cacheVariables": { "CMAKE_BUILD_TYPE": "Release" @@ -22,23 +20,107 @@ }, { "name": "Default-Debug", - "description": "Debug build", "inherits": "Base", "cacheVariables": { "CMAKE_BUILD_TYPE": "Debug" } + }, + { + "name": "Default-RelWithDebInfo", + "inherits": "Base", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "RelWithDebInfo" + } + }, + { + "name": "Default-Sanitized", + "inherits": "Base", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Release", + "SANITIZED": "ON" + } + }, + { + "name": "Default-SanitizedDebug", + "inherits": "Base", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug", + "SANITIZED": "ON" + } } ], "buildPresets": [ { "name": "Default-Release", - "description": "Build Release configuration", "configurePreset": "Default-Release" }, { "name": "Default-Debug", - "description": "Build Debug configuration", "configurePreset": "Default-Debug" + }, + { + "name": "Default-RelWithDebInfo", + "configurePreset": "Default-RelWithDebInfo" + }, + { + "name": "Default-Sanitized", + "configurePreset": "Default-Sanitized" + }, + { + "name": "Default-SanitizedDebug", + "configurePreset": "Default-SanitizedDebug" + } + ], + "testPresets": [ + { + "name": "Default-Release", + "configurePreset": "Default-Release", + "execution": { + "jobs": 16 + }, + "output": { + "outputOnFailure": true + } + }, + { + "name": "Default-Debug", + "configurePreset": "Default-Debug", + "execution": { + "jobs": 16 + }, + "output": { + "outputOnFailure": true + } + }, + { + "name": "Default-RelWithDebInfo", + "configurePreset": "Default-RelWithDebInfo", + "execution": { + "jobs": 16 + }, + "output": { + "outputOnFailure": true + } + }, + { + "name": "Default-Sanitized", + "configurePreset": "Default-Sanitized", + "execution": { + "jobs": 16 + }, + "output": { + "outputOnFailure": true + } + }, + { + "name": "Default-SanitizedDebug", + "configurePreset": "Default-SanitizedDebug", + "execution": { + "jobs": 16 + }, + "output": { + "outputOnFailure": true + } } ] } diff --git a/README.md b/README.md index 875e690..96c2592 100644 --- a/README.md +++ b/README.md @@ -5,23 +5,13 @@ Protea (named after Proteus, the Greek sea god known for his ability to change s ProteaIR (Protea Intermediate Representation) doesn't have canonical textual or binary representation. Instead, it is possible to serialize and deserialize it using JSON, YAML, or any other format. -Usage +Building Protea ----- -It is a monorepo managed by [Bundler](https://bundler.io/). To install dependencies, run: - -```bash -bundle install -``` - -So every ruby tool or script can be run using `bundle exec`, for example: - ```bash -bundle exec ruby tools/some_tool.rb +cmake -S . -B build -G [options] ``` - - -Tests ------ - - +Some common options: +1) -DPROTEA_BUILD_TESTS=BOOL - An option to enable tests. Tests can be run using ctests +2) -DTARGET_NAME_TOOLCHAIN_DIR="..." - A path to target's toolchain dir +3) -DQEMU_PATH="..." - A path to qemu executable diff --git a/ci-extra/test.sh b/ci-extra/test.sh new file mode 100755 index 0000000..c04a4be --- /dev/null +++ b/ci-extra/test.sh @@ -0,0 +1,11 @@ +PRESET_NAME=$1 + +cmake -S . \ + --preset "${PRESET_NAME}" \ + -DPROTEA_BUILD_TESTS=true \ + -DQEMU_PATH=qemu-riscv32 \ + -G Ninja + +cmake --build --preset "${PRESET_NAME}" --target simtests --parallel 12 + +ctest --preset "${PRESET_NAME}" diff --git a/cmake/CompilerConfig.cmake b/cmake/CompilerConfig.cmake new file mode 100644 index 0000000..e7b2bff --- /dev/null +++ b/cmake/CompilerConfig.cmake @@ -0,0 +1,60 @@ +option(HARDENED "Should the standard library be hardened" OFF) +option(SANITIZED "Should the build be sanitized" OFF) + +function(configure_compiler) + set(isGCC OFF) + set(isClang OFF) + + if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + set(isGCC ON) + elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang") + set(isClang ON) + endif() + + set(compilerOptions "") + set(compilerDefinitions "") + set(linkerOptions "") + + if(isClang) + list(APPEND compilerOptions -stdlib=libc++) + list(APPEND linkerOptions -stdlib=libc++) + message(STATUS "Using libc++ as a standard library") + endif() + + if(HARDENED) + if(isGCC) + list(APPEND compilerDefinitions _GLIBCXX_DEBUG) + message(STATUS "Enabled debug mode for libstdc++") + elseif(isClang) + list(APPEND compilerDefinitions _LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_DEBUG) + message(STATUS "Enabled hardening mode for libc++") + else() + message(STATUS "Hardening is not supported for CXX compiler: '${CMAKE_CXX_COMPILER_ID}'") + endif() + endif() + + if(SANITIZED) + if(isGCC OR isClang) + list(APPEND compilerOptions + -fsanitize=undefined,address + -fno-sanitize-recover=all + -fno-optimize-sibling-calls + -fno-omit-frame-pointer + ) + list(APPEND linkerOptions + -fsanitize=undefined,address + ) + message(STATUS "Enabled UBSan and ASan") + else() + message(WARNING "Sanitized builds are not supported for CXX compiler: '${CMAKE_CXX_COMPILER_ID}'") + endif() + endif() + + message(STATUS "Setting global compiler options: ${compilerOptions}") + message(STATUS "Setting global compiler definitions: ${compilerDefinitions}") + message(STATUS "Setting global linker options: ${linkerOptions}") + + add_compile_options(${compilerOptions}) + add_compile_definitions(${compilerDefinitions}) + add_link_options(${linkerOptions}) +endfunction() diff --git a/cmake/QEMU.cmake b/cmake/QEMU.cmake new file mode 100644 index 0000000..bdc5122 --- /dev/null +++ b/cmake/QEMU.cmake @@ -0,0 +1,5 @@ +if (NOT DEFINED QEMU_PATH) + set(QEMU_PATH "qemu") +endif() + +message(STATUS "Using QEMU: ${QEMU_PATH}") diff --git a/cmake/discover_generated_tests.cmake.in b/cmake/discover_generated_tests.cmake.in new file mode 100644 index 0000000..5b0b616 --- /dev/null +++ b/cmake/discover_generated_tests.cmake.in @@ -0,0 +1,28 @@ +if(NOT EXISTS "@OUTPUT_TESTS_DIR@") + message(STATUS "Test directory does not exist yet: @OUTPUT_TESTS_DIR@") + return() +endif() + +file(GLOB discovered_tests "@OUTPUT_TESTS_DIR@/*") + +if(NOT discovered_tests) + message(STATUS "No generated tests found in @OUTPUT_TESTS_DIR@") +endif() + +foreach(test_file IN LISTS discovered_tests) + if(IS_DIRECTORY "${test_file}") + continue() + endif() + + get_filename_component(test_name "${test_file}" NAME) + + add_test( + "@PROJECT_NAME@.${test_name}" + "@GENERATED_SIM_BIN_PATH@" --propagate-exit "${test_file}" + ) + + add_test( + "@PROJECT_NAME@.cached-interp.${test_name}" + "@GENERATED_SIM_BIN_PATH@" --jit cached-interp --propagate-exit "${test_file}" + ) +endforeach() diff --git a/cmake/toolchain/riscv.cmake b/cmake/toolchain/riscv.cmake new file mode 100644 index 0000000..67e51d5 --- /dev/null +++ b/cmake/toolchain/riscv.cmake @@ -0,0 +1,65 @@ +# RISC-V Cross Compilation Toolchain File Usage: cmake +# -DCMAKE_TOOLCHAIN_FILE=path/to/this/file.cmake .. + +# Base settings +set(CMAKE_SYSTEM_NAME Generic) +set(CMAKE_SYSTEM_PROCESSOR riscv) +set(CMAKE_LINKER_TYPE DEFAULT) + +# Toolchain prefix (modify this according to your toolchain) Common prefixes: +# riscv64-unknown-elf-, riscv64-unknown-linux-gnu-, riscv32-unknown-elf- +set(RISCV_TOOLCHAIN_PREFIX + "riscv32-unknown-elf-" + CACHE STRING "RISC-V toolchain prefix") + +# Target architecture (modify these according to your needs) +set(RISCV_ARCH + "rv32im" + CACHE STRING "RISC-V architecture") +set(RISCV_ABI + "ilp32" + CACHE STRING "RISC-V ABI") + +# Cross-compilation tools +if(DEFINED RISCV_TOOLCHAIN_DIR) + set(CMAKE_C_COMPILER "${RISCV_TOOLCHAIN_DIR}/${RISCV_TOOLCHAIN_PREFIX}gcc") + set(CMAKE_CXX_COMPILER "${RISCV_TOOLCHAIN_DIR}/${RISCV_TOOLCHAIN_PREFIX}g++") + set(CMAKE_ASM_COMPILER "${RISCV_TOOLCHAIN_DIR}/${RISCV_TOOLCHAIN_PREFIX}gcc") + set(CMAKE_AR "${RISCV_TOOLCHAIN_DIR}/${RISCV_TOOLCHAIN_PREFIX}ar") + set(CMAKE_RANLIB "${RISCV_TOOLCHAIN_DIR}/${RISCV_TOOLCHAIN_PREFIX}ranlib") +else() + set(CMAKE_C_COMPILER "${RISCV_TOOLCHAIN_PREFIX}gcc") + set(CMAKE_CXX_COMPILER "${RISCV_TOOLCHAIN_PREFIX}g++") + set(CMAKE_ASM_COMPILER "${RISCV_TOOLCHAIN_PREFIX}gcc") + set(CMAKE_AR "${RISCV_TOOLCHAIN_PREFIX}ar") + set(CMAKE_RANLIB "${RISCV_TOOLCHAIN_PREFIX}ranlib") +endif() + +# Compiler flags Search settings +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) + +# Optional: Specify sysroot if needed set(CMAKE_SYSROOT +# "/path/to/riscv/sysroot") + +# Optional: Additional flags for specific use cases +set(COMMON_FLAGS + "-march=${RISCV_ARCH} -mabi=${RISCV_ABI} -Wall -Wextra -nostdlib -nodefaultlibs -nostartfiles -static -fno-builtin" +) +set(CMAKE_C_FLAGS + "${COMMON_FLAGS}" + CACHE STRING "C compiler flags") +set(CMAKE_ASM_FLAGS + "${COMMON_FLAGS}" + CACHE STRING "ASM compiler flags") + +set(CMAKE_EXE_LINKER_FLAGS_INIT + "-nostdlib -nodefaultlibs -nostartfiles -fno-builtin -static" + CACHE STRING "Linker flags") + +# Test compiler +if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.6) + set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) +endif() diff --git a/code_gen/cpp_gen.rb b/code_gen/cpp_gen.rb index 4388b34..183f1f9 100644 --- a/code_gen/cpp_gen.rb +++ b/code_gen/cpp_gen.rb @@ -52,6 +52,8 @@ def generate_statement(operation) binary_operation(@emitter, operation, '-') when :mul binary_operation(@emitter, operation, '*') + when :rem + binary_operation(@emitter, operation, '%') when :div binary_operation(@emitter, operation, '/') when :shr @@ -83,7 +85,7 @@ def generate_statement(operation) @emitter.emit_line("#{dst} = #{src};") when :new_var var_name = operation[:oprnds][0][:name] - var_type = Utility::HelperCpp.gen_type(operation[:oprnds][0][:type]) + var_type = Utility::HelperCpp.gen_small_type(operation[:oprnds][0][:type]) @emitter.emit_line("#{var_type} #{var_name};") when :cast dst = @mapping[operation[:oprnds][0][:name]] || operation[:oprnds][0][:name] @@ -134,7 +136,8 @@ def generate_statement(operation) cond = @mapping[operation[:oprnds][1][:name]] || operation[:oprnds][1][:name] true_val = @mapping[operation[:oprnds][2][:name]] || operation[:oprnds][2][:name] false_val = @mapping[operation[:oprnds][3][:name]] || operation[:oprnds][3][:name] - + true_val = true_val.nil? ? operation[:oprnds][2][:value] : true_val + false_val = false_val.nil? ? operation[:oprnds][3][:value] : false_val @emitter.emit_line("#{dst} = #{cond} ? #{true_val} : #{false_val};") end end diff --git a/lib/ADL/base.rb b/lib/ADL/base.rb index 8587824..4f0a607 100644 --- a/lib/ADL/base.rb +++ b/lib/ADL/base.rb @@ -4,11 +4,33 @@ module SimInfra # @@instructions -array of instruction description # shows result of our tests in interactive Ruby (IRB) or standalone + @@regfiles = [] unless defined?(@@regfiles) + @@interface_functions = [] unless defined?(@@interface_functions) + @@instructions = [] unless defined?(@@instructions) + + def self.register_memory_interface(name, return_types, argument_types) + # check whether the interface has already been registered before + exists = @@interface_functions.any? do |f| + f[:name] == name && + f[:return_types] == return_types && + f[:argument_types] == argument_types + end + unless exists + @@interface_functions << { + name: name, + return_types: return_types, + argument_types: argument_types, + kind: :memory + } + end + end + def self.serialize(msg= nil) require 'yaml' yaml_data = YAML.dump( { regfiles: @@regfiles.map(&:to_h), + interface_functions: @@interface_functions.map(&:to_h), instructions: @@instructions.map(&:to_h), } ) @@ -16,16 +38,17 @@ def self.serialize(msg= nil) end def self.state - yaml_data = YAML.dump( + YAML.dump( { regfiles: @@regfiles.map(&:to_h), + interface_functions: @@interface_functions.map(&:to_h), instructions: @@instructions.map(&:to_h), } ) end # reset state - def siminfra_reset_module_state; @@instructions = []; end + def siminfra_reset_module_state; @@instructions = []; @@operands = []; end # mixin for global counter, function returns 0,1,2,.... module GlobalCounter diff --git a/lib/ADL/builder.rb b/lib/ADL/builder.rb index 83d0313..b7fe22d 100644 --- a/lib/ADL/builder.rb +++ b/lib/ADL/builder.rb @@ -29,22 +29,26 @@ def self.from_h(h) end # Basics -module SimInfra +module SimInfra def assert(condition, msg = nil); raise msg if !condition; end @@instructions = [] @@interface_functions = [] + @@operands = [] def self.interface_functions @@interface_functions end class InstructionInfo - attr_accessor :name, :fields, :frmt, :map, :code, :map_code_blocks, :asm_str, :XLEN, :feature + attr_accessor :name, :fields, :frmt, :map, :code, :map_code_blocks, :asm_str, :feature, + :operand_map, :operand_list def initialize(name, feature) @name = name; @map_code_blocks = {} @feature = feature + @operand_map = {} + @operand_list = [] end def to_h @@ -52,10 +56,10 @@ def to_h name: @name, fields: @fields.map { |f| f.to_h }, frmt: @frmt, - XLEN: @XLEN, asm_str: @asm_str, code: @code.to_h, - map: @map.to_h, + operand_map: @operand_map.transform_values(&:to_h), + operand_list: @operand_list, feature: @feature, } end @@ -64,28 +68,53 @@ def self.from_h(h) info = InstructionInfo.new(h[:name], h[:feature]) info.fields = h[:fields].map { |f| Field.from_h(f) } info.frmt = h[:frmt] - info.XLEN = h[:XLEN] info.asm_str = h[:asm_str] info.code = Scope.new(nil) info.code.instance_variable_set(:@tree, h[:code][:tree].map { |s| IrStmt.from_h(s) }) - info.map = Scope.new(nil) - info.map.instance_variable_set(:@tree, h[:map][:tree].map { |s| IrStmt.from_h(s) }) + if h[:operand_map] + info.operand_map = h[:operand_map].transform_values { |v| Scope.from_h(v) } + info.operand_list = h[:operand_list] || info.operand_map.keys + end + info + end + end + + class OperandInfo + attr_accessor :name, :type, :map_proc, :asm_str, :sem_proc + + def initialize(name, type = nil) + @name = name + @type = type + @map_proc = nil + @asm_str = nil + @sem_proc = nil + end + + def for(op_name) + template = self + info = OperandInfo.new(op_name, @type) + info.asm_str = @asm_str + info.sem_proc = @sem_proc + info.map_proc = proc { instance_exec(op_name, &template.map_proc) } info end + + alias_method :[], :for end class InstructionInfoBuilder include SimInfra - def initialize(name, feature) - @info = InstructionInfo.new(name, feature) - @info.code = Scope.new(nil) + def initialize(name, feature) + @info = InstructionInfo.new(name, feature) + @info.code = Scope.new(nil) @@interface_functions.each do |func| + next if func[:kind] == :memory if !func[:return_types].empty? @info.code.instance_eval "def #{func[:name]}(*args) in_s = *args.map { |a| resolve_const(a) } - in_stmt = [tmpvar(#{func[:return_types][0]})] + in_stmt = [tmpvar(#{func[:return_types][0]})] in_stmt.concat(in_s) return stmt :#{func[:name]}, in_stmt end @@ -103,16 +132,14 @@ def initialize(name, feature) end def encoding(frmt, fields, *args) - @info.fields = fields - @info.frmt= frmt - map args - + @info.fields = fields + @info.frmt= frmt + map args + sum_bits = 0 for f in fields sum_bits += Utility.get_type(f.value.type).bitsize end - @info.XLEN = sum_bits / 8 - @info.code.instance_eval "def xlen(); return #{@info.XLEN.to_s}; end" end attr_reader :info end @@ -126,17 +153,52 @@ def Instruction(name, &block) nil # only for debugging in IRB end + class OperandBuilder + include SimInfra + + def initialize(name, type = nil) + @info = OperandInfo.new(name, type) + end + + def map(type = nil, &block) + @info.type = type if type + @info.map_proc = block if block + end + + def asm(&block) + @info.asm_str = instance_eval(&block) if block + end + + def code(&block) + @info.map_proc = block if block + end + + def sem(&block) + @info.sem_proc = block if block + end + + attr_reader :info + end + + def Operand(name, type = nil, &block) + bldr = OperandBuilder.new(name, type) + bldr.instance_eval(&block) if block + @@operands << bldr.info + bldr.info + end + + module_function :Operand + class InterfaceBuilder include SimInfra - def function(name, output_types = [], input_types = []) + def Function(name, output_types = [], input_types = []) @@interface_functions << {:name => name, :return_types => output_types, :argument_types => input_types} end end def Interface(&blck) bldr = InterfaceBuilder.new() - bldr.instance_eval &blck end @@ -181,11 +243,25 @@ def self.from_h(h) @@regfiles = [] class RegisterFileBuilder - def initialize(name) - @info = RegisterFileInfo.new(name) - @info.regs = [] + attr_reader :info + + def initialize(name) + @info = RegisterFileInfo.new(name) + @info.regs = [] + end + + def method_missing(name, *args) + if name.to_s.start_with?('r') + size = name.to_s[1..].to_i + instance_eval "def #{name}(sym, *args); @info.regs << Register.new(sym, #{size}, args.size > 0 ? args : []); end", __FILE__, __LINE__ + @info.regs << Register.new(args[0], size, args.size > 1 ? args[1..] : []) + else + error "Unknown method #{name}" end - attr_reader :info + end + + def zero = :zero + def pc = :pc end def RegisterFile(name, &block) @@ -202,33 +278,15 @@ def RegFiles() # * generate precise fields module SimInfra - class RegisterFileBuilder - def r32(sym, *args) - @info.regs << Register.new(sym, 32, args[0] ? [args[0]] : []) - end - - def zero() - :zero - end - - def pc() - :pc - end - end - class InstructionInfoBuilder def code(&block) - if !@info.map_code_blocks.empty? - @info.fields.each { |f| - @info.map.method(f.value.name, f.value.type) - } - end - @info.map_code_blocks.each do |k, v| - @info.map.instance_eval v[1] - end - @info.map_code_blocks.each do |k, v| - @info.code.method(k, v[0], @info.map.vars[k].regset) + # Link code scope methods to operand vars + @info.operand_map.each do |name, scope| + var = scope.vars[name] + @info.code.method(name, var.type, var.regset) if var end + + # Register files for regfile in @@regfiles for reg in regfile.regs @info.code.method(reg.name, ('r' + reg.size.to_s).to_sym) @@ -238,11 +296,31 @@ def code(&block) end def map(blocks) - if (!blocks.nil? && !blocks.empty?) - for blck in blocks - @info.map_code_blocks[blck[0]] = [blck[1], blck[2]] - end + return if blocks.nil? || blocks.empty? + + blocks.each do |block| + scope = build_scope(block) + name = operand_name(block) + @info.operand_map[name] = scope + end + @info.operand_list = blocks.map { |b| operand_name(b) } + end + + private + + def operand_name(block) + block.is_a?(OperandInfo) ? block.name : block[0] + end + + def build_scope(block) + scope = Scope.new(nil) + @info.fields.each { |f| scope.method(f.value.name, f.value.type) } + if block.is_a?(OperandInfo) + scope.instance_eval(&block.map_proc) + else + scope.instance_eval(block[2]) end + scope end def asm(&block) diff --git a/lib/ADL/scope.rb b/lib/ADL/scope.rb index 57d4643..0ebd1d6 100644 --- a/lib/ADL/scope.rb +++ b/lib/ADL/scope.rb @@ -75,6 +75,9 @@ def binOpWType(a, b, op, t) # redefine! add & sub will never be the same def add(a, b) = binOp(a, b, :add) def sub(a, b) = binOp(a, b, :sub) + def mul(a, b) = binOp(a, b, :mul) + def div(a, b) = binOp(a, b, :div) + def rem(a, b) = binOp(a, b, :rem) def shl(a, b) = binOp(a, b, :shl) def lt(a, b) = binOpWType(a, b, :lt, :b1) def gt(a, b) = binOpWType(a, b, :gt, :b1) @@ -103,8 +106,33 @@ def zext(a, type) = stmt(:zext, [tmpvar(type), a]) def get_reg(expr, regset, type) = rlet("_reg_#{next_counter}".to_sym, regset, type, expr) def write(rfile, reg, expr) = stmt(:write, [rfile, reg, expr]) - def writeMem(addr, expr) = stmt(:writeMem, [addr, expr]) - def readMem(addr, type) = stmt(:readMem, [tmpvar(type), addr]) + + def memory_interface_name(base_name, types) + suffix = nil + if base_name == :readMem + type = types.first + suffix = type.to_s.match(/\d+/)[0] if type && type.to_s =~ /b\d+|r\d+/ + elsif base_name == :writeMem + type = types[1] if types.size > 1 + suffix = type.to_s.match(/\d+/)[0] if type && type.to_s =~ /b\d+|r\d+/ + end + suffix ? "#{base_name}#{suffix}".to_sym : base_name + end + + def writeMem(addr, expr) + arg_types = [addr.type, expr.type] + iface_name = memory_interface_name(:writeMem, arg_types) + SimInfra.register_memory_interface(iface_name, [], arg_types) + stmt(:writeMem, [addr, expr], iface_name) + end + + def readMem(addr, type) + arg_types = [addr.type] + iface_name = memory_interface_name(:readMem, [type]) + iface = SimInfra.register_memory_interface(iface_name, [type], arg_types) + v = tmpvar(type) + stmt(:readMem, [v, addr], iface_name) + end def read(rfile, reg) v = tmpvar(:b32) diff --git a/lib/ADL/var.rb b/lib/ADL/var.rb index 671f258..63321f5 100644 --- a/lib/ADL/var.rb +++ b/lib/ADL/var.rb @@ -42,6 +42,9 @@ module SimInfra class Var def +(other) = @scope.add(self, other) def -(other) = @scope.sub(self, other) + def *(other) = @scope.mul(self, other) + def /(other) = @scope.div(self, other) + def %(other) = @scope.rem(self, other) def <<(other) = @scope.shl(self, other) def <(other) = @scope.lt(self, other) def <=(other) = @scope.le(self, other) diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index f637db6..91e289d 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -1,9 +1,56 @@ project(protea_irgen) +set(NATIVE_IR_FILE ${CMAKE_CURRENT_BINARY_DIR}/IR.yaml) + add_custom_command( - OUTPUT IR.yaml + OUTPUT ${NATIVE_IR_FILE} + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/ir_gen.rb + ${CMAKE_CURRENT_SOURCE_DIR}/ADL/base.rb + ${CMAKE_CURRENT_SOURCE_DIR}/ADL/builder.rb + ${CMAKE_CURRENT_SOURCE_DIR}/Target/RISC-V/encoding.rb + ${CMAKE_CURRENT_SOURCE_DIR}/Target/RISC-V/32I.rb + ${CMAKE_CURRENT_SOURCE_DIR}/Target/RISC-V/32M.rb COMMAND ${BUNDLE_PATH} exec ruby ${CMAKE_CURRENT_SOURCE_DIR}/ir_gen.rb - COMMENT "Running ${PROJECT_NAME}" + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMENT "Generating native IR (IR.yaml)" +) + +add_custom_target(native_gen DEPENDS ${NATIVE_IR_FILE}) + +set(LIRA_IR_FILE ${CMAKE_CURRENT_BINARY_DIR}/lira.yaml) + +add_custom_command( + OUTPUT ${LIRA_IR_FILE} + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/lira_gen.rb + ${CMAKE_CURRENT_SOURCE_DIR}/ADL/base.rb + ${CMAKE_CURRENT_SOURCE_DIR}/ADL/builder.rb + ${CMAKE_CURRENT_SOURCE_DIR}/ADL/scope.rb + ${CMAKE_CURRENT_SOURCE_DIR}/ADL/var.rb + ${CMAKE_CURRENT_SOURCE_DIR}/ADL/value.rb + ${CMAKE_CURRENT_SOURCE_DIR}/Target/RISC-V/encoding.rb + ${CMAKE_CURRENT_SOURCE_DIR}/Target/RISC-V/32I.rb + ${CMAKE_CURRENT_SOURCE_DIR}/Target/RISC-V/32M.rb + ${CMAKE_CURRENT_SOURCE_DIR}/lira/arch.rb + ${CMAKE_CURRENT_SOURCE_DIR}/lira/ir.rb + ${CMAKE_CURRENT_SOURCE_DIR}/lira/ir_builder.rb + ${CMAKE_CURRENT_SOURCE_DIR}/lira/ir_ops.rb + ${CMAKE_CURRENT_SOURCE_DIR}/lira/arch_ser_yaml.rb + ${CMAKE_CURRENT_SOURCE_DIR}/lira/ir_ser_txt.rb + ${CMAKE_CURRENT_SOURCE_DIR}/Utility/lira_utils.rb + ${CMAKE_CURRENT_SOURCE_DIR}/Utility/type.rb + COMMAND ${BUNDLE_PATH} exec ruby ${CMAKE_CURRENT_SOURCE_DIR}/lira_gen.rb + --target ${CMAKE_SOURCE_DIR}/lib/Target/RISC-V/ + --output ${LIRA_IR_FILE} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMENT "Generating LIRA IR (lira.yaml)" ) -add_custom_target(${PROJECT_NAME} DEPENDS IR.yaml) -install(FILES ${CMAKE_CURRENT_BINARY_DIR}/IR.yaml DESTINATION share/) + +add_custom_target(lira_gen DEPENDS ${LIRA_IR_FILE}) + +add_custom_target(protea_irgen DEPENDS native_gen) + +set(NATIVE_IR_FILE ${NATIVE_IR_FILE} PARENT_SCOPE) +set(LIRA_IR_FILE ${LIRA_IR_FILE} PARENT_SCOPE) + +install(FILES ${NATIVE_IR_FILE} DESTINATION share/) +install(FILES ${LIRA_IR_FILE} DESTINATION share/) diff --git a/lib/Target/RISC-V/32I.rb b/lib/Target/RISC-V/32I.rb index 6a67a7b..e0de18c 100644 --- a/lib/Target/RISC-V/32I.rb +++ b/lib/Target/RISC-V/32I.rb @@ -7,7 +7,7 @@ module RV32I extend SimInfra Interface { - function :sysCall + Function(:sysCall) } RegisterFile(:XRegs) { @@ -127,7 +127,7 @@ module RV32I Instruction(:slti) { encoding *format_i(0b0010011, 0b010) asm { "slti {rd}, {rs1}, {imm}" } - code { rd[]= (rs1.s < imm).b32 } + code { rd[]= (rs1.s < imm.s).b32 } } Instruction(:sltiu) { @@ -155,19 +155,19 @@ module RV32I } Instruction(:slli) { - encoding *format_i_shift(0b0010011, 0b001, 0b00000) + encoding *format_i_shift(0b0010011, 0b001, 0b0000000) asm { "slli {rd}, {rs1}, {imm}" } code { rd[]= rs1 << imm } } Instruction(:srli) { - encoding *format_i_shift(0b0010011, 0b101, 0b00000) + encoding *format_i_shift(0b0010011, 0b101, 0b0000000) asm { "srli {rd}, {rs1}, {imm}" } code { rd[]= rs1 >> imm } } Instruction(:srai) { - encoding *format_i_shift(0b0010011, 0b101, 0b01000) + encoding *format_i_shift(0b0010011, 0b101, 0b0100000) asm { "srai {rd}, {rs1}, {imm}" } code { rd[]= rs1.s >> imm } } @@ -175,50 +175,50 @@ module RV32I Instruction(:beq) { encoding *format_b(0b1100011, 0b000) asm { "beq {rs1}, {rs2}, {imm}" } - code { branch(select(rs1 == rs2, pc + imm, pc + xlen)) } + code { branch(select(rs1 == rs2, pc + imm, pc + 4)) } } Instruction(:bne) { encoding *format_b(0b1100011, 0b001) asm { "bne {rs1}, {rs2}, {imm}" } - code { branch(select(rs1 != rs2, pc + imm, pc + xlen)) } + code { branch(select(rs1 != rs2, pc + imm, pc + 4)) } } Instruction(:blt) { encoding *format_b(0b1100011, 0b100) asm { "blt {rs1}, {rs2}, {imm}" } - code { branch(select(rs1.s < rs2.s, pc + imm, pc + xlen)) } + code { branch(select(rs1.s < rs2.s, pc + imm, pc + 4)) } } Instruction(:bge) { encoding *format_b(0b1100011, 0b101) asm { "bge {rs1}, {rs2}, {imm}" } - code { branch(select(rs1.s >= rs2.s, pc + imm, pc + xlen)) } + code { branch(select(rs1.s >= rs2.s, pc + imm, pc + 4)) } } Instruction(:bltu) { encoding *format_b(0b1100011, 0b110) asm { "bltu {rs1}, {rs2}, {imm}" } - code { branch(select(rs1.u < rs2.u, pc + imm, pc + xlen)) } + code { branch(select(rs1.u < rs2.u, pc + imm, pc + 4)) } } Instruction(:bgeu) { encoding *format_b(0b1100011, 0b111) asm { "bgeu {rs1}, {rs2}, {imm}" } - code { branch(select(rs1.u >= rs2.u, pc + imm, pc + xlen)) } + code { branch(select(rs1.u >= rs2.u, pc + imm, pc + 4)) } } Instruction(:jal) { encoding *format_j(0b1101111) asm { "jal {rd}, {imm}" } - code { rd[]= pc + xlen; branch(pc + imm) } + code { rd[]= pc + 4; branch(pc + imm) } } Instruction(:jalr) { encoding *format_i(0b1100111, 0b000) asm { "jalr {rd}, {rs1}, {imm}" } - code { - let :t, :b32, pc + xlen + code { + let :t, :b32, pc + 4 branch((rs1 + imm) & (~1)) rd[]= t } @@ -259,7 +259,7 @@ module RV32I asm { "lw {rd}, {imm}({rs1})" } code { rd[]= mem[rs1 + imm, :b32] } } - + Instruction(:lbu) { encoding *format_i(0b0000011, 0b100) asm { "lbu {rd}, {imm}({rs1})" } diff --git a/lib/Target/RISC-V/32ILoops.rb b/lib/Target/RISC-V/32ILoops.rb deleted file mode 100644 index 07b0b06..0000000 --- a/lib/Target/RISC-V/32ILoops.rb +++ /dev/null @@ -1,194 +0,0 @@ -require_relative "encoding" -require_relative "../../Generic/base" -require_relative "../../Generic/builder" - -module Ops - - class Add; def self.op(a, b); a.u + b.u; end; end - class Sub; def self.op(a, b); a.u - b.u; end; end - class Sll; def self.op(a, b); a.u << b.u; end; end - class Slt; def self.op(a, b); (a.s < b.s).b; end; end - class Sltu; def self.op(a, b); (a.u < b.u).b; end; end - class Xor; def self.op(a, b); a ^ b; end; end - class Srl; def self.op(a, b); a.u >> b.u; end; end - class Sra; def self.op(a, b); a.s >> b.u; end; end - class Or; def self.op(a, b); a | b; end; end - class And; def self.op(a, b); a & b; end; end - class Load8; def self.op(rs1, imm); mem[rs1 + imm, :b8].s32; end; end - class Load16; def self.op(rs1, imm); mem[rs1 + imm, :b16].s32; end; end - class Load32; def self.op(rs1, imm); mem[rs1 + imm, :b32]; end; end - class Load8U; def self.op(rs1, imm); mem[rs1 + imm, :b8].u32; end; end - class Load16U; def self.op(rs1, imm); mem[rs1 + imm, :b16].u32; end; end - -end - -module RV32I - include SimInfra - extend SimInfra - - RegisterFile(:XRegs) { - r32 :x0, zero - for x in (1..31); r32 "x" + x.to_s; end - r32 :pc - } - - Instruction(:lui) { - encoding *format_u(0b0110111) - asm { "lui {rd}, {imm}" } - code { rd[]= imm } - } - - Instruction(:auipc) { - encoding *format_u(0b0010111) - asm { "auipc {rd}, {imm}" } - code { rd[]= imm + pc } - } - - TABLE_R_FORMAT_INSTRUCTIONS = [ - [:add, format_r(0b0110011, 0b000, 0b0000000), Ops::Add], - [:sub, format_r(0b0110011, 0b000, 0b0100000), Ops::Sub], - [:sll, format_r(0b0110011, 0b001, 0b0000000), Ops::Sll], - [:slt, format_r(0b0110011, 0b010, 0b0000000), Ops::Slt], - [:sltu, format_r(0b0110011, 0b011, 0b0000000), Ops::Sltu], - [:xor, format_r(0b0110011, 0b100, 0b0000000), Ops::Xor], - [:srl, format_r(0b0110011, 0b101, 0b0000000), Ops::Srl], - [:sra, format_r(0b0110011, 0b101, 0b0100000), Ops::Sra], - [:or, format_r(0b0110011, 0b110, 0b0000000), Ops::Or], - [:and, format_r(0b0110011, 0b111, 0b0000000), Ops::And], - ] - - for insn in TABLE_R_FORMAT_INSTRUCTIONS - Instruction(insn[0]) { - encoding *insn[1] - asm { insn[0].to_s + "{rd}, {rs1}, {rs2}" } - code { rd[]= insn[2].op(rs1, rs2) } - } - end - - TABLE_I_FORMAT_INSTRUCTIONS = [ - [:addi, format_i(0b0010011, 0b000), Ops::Add], - [:xori, format_i(0b0010011, 0b100), Ops::Xor], - [:ori, format_i(0b0010011, 0b110), Ops::Or], - [:andi, format_i(0b0010011, 0b111), Ops::And], - ] - - for insn in TABLE_I_FORMAT_INSTRUCTIONS - Instruction(insn[0]) { - encoding *insn[1] - asm { insn[0].to_s + "{rd}, {rs1}, {imm}" } - code { rd[]= insn[2].op(rs1, imm) } - } - end - - Instruction(:beq) { - encoding *format_b(0b1100011, 0b000) - asm { "beq {rs1}, {rs2}, {imm}" } - code { branch(select(rs1 == rs2, pc + imm, pc + xlen)) } - } - - Instruction(:bne) { - encoding *format_b(0b1100011, 0b001) - asm { "bne {rs1}, {rs2}, {imm}" } - code { branch(select(rs1 != rs2, pc + imm, pc + xlen)) } - } - - Instruction(:blt) { - encoding *format_b(0b1100011, 0b100) - asm { "blt {rs1}, {rs2}, {imm}" } - code { branch(select(rs1 < rs2, pc + imm, pc + xlen)) } - } - - Instruction(:bge) { - encoding *format_b(0b1100011, 0b101) - asm { "bge {rs1}, {rs2}, {imm}" } - code { branch(select(rs1 > rs2, pc + imm, pc + xlen)) } - } - - Instruction(:bltu) { - encoding *format_b(0b1100011, 0b110) - asm { "bltu {rs1}, {rs2}, {imm}" } - code { branch(select(rs1.u < rs2.u, pc + imm, pc + xlen)) } - } - - Instruction(:bgeu) { - encoding *format_b(0b1100011, 0b111) - asm { "bgeu {rs1}, {rs2}, {imm}" } - code { branch(select(rs1.u > rs2.u, pc + imm, pc + xlen)) } - } - - Instruction(:jal) { - encoding *format_j(0b1101111) - asm { "jal {rd}, {imm}" } - code { rd[]= pc + xlen; branch(pc + imm) } - } - - Instruction(:jalr) { - encoding *format_i(0b1101111, 0b000) - asm { "jalr {rd}, {rs1}, {imm}" } - code { - let :t, :b32, pc + xlen - branch((rs1 + imm) & (~1)) - rd[]= t - } - } - - Instruction(:sb) { - encoding *format_s(0b0100011, 0b000) - asm { "sb {rs2}, {imm}({rs1})" } - code { mem[rs1 + imm]= rs2[7, 0] } - } - - Instruction(:sh) { - encoding *format_s(0b0100011, 0b001) - asm { "sh {rs2}, {imm}({rs1})" } - code { mem[rs1 + imm]= rs2[15, 0] } - } - - Instruction(:sw) { - encoding *format_s(0b0100011, 0b010) - asm { "sw {rs2}, {imm}({rs1})" } - code { mem[rs1 + imm]= rs2 } - } - - Instruction(:lb) { - encoding *format_i(0b0100011, 0b000) - asm { "lb {rd}, {imm}({rs1})" } - code { rd[]= mem[rs1 + imm, :b8].s32 } - } - - Instruction(:lh) { - encoding *format_i(0b0100011, 0b001) - asm { "lh {rd}, {imm}({rs1})" } - code { rd[]= mem[rs1 + imm, :b16].s32 } - } - - Instruction(:lw) { - encoding *format_i(0b0100011, 0b010) - asm { "lw {rd}, {imm}({rs1})" } - code { rd[]= mem[rs1 + imm, :b32] } - } - - Instruction(:lbu) { - encoding *format_i(0b0100011, 0b100) - asm { "lbu {rd}, {imm}({rs1})" } - code { rd[]= mem[rs1 + imm, :b8].u32 } - } - - Instruction(:lhu) { - encoding *format_i(0b0100011, 0b101) - asm { "lhu {rd}, {imm}({rs1})" } - code { rd[]= mem[rs1 + imm, :b16].u32 } - } - - Instruction(:ecall) { - encoding :E, [field(:c, 31, 0, 0b1110011)] - asm { "ecall" } - code { } - } - - Instruction(:ebreak) { - encoding :E, [field(:c, 31, 0, 0b100000000000001110011)] - asm { "ebreak" } - code { } - } -end diff --git a/lib/Target/RISC-V/32M.rb b/lib/Target/RISC-V/32M.rb new file mode 100644 index 0000000..dcbc8fa --- /dev/null +++ b/lib/Target/RISC-V/32M.rb @@ -0,0 +1,56 @@ +require_relative "encoding" +require_relative "../../ADL/base" +require_relative "../../ADL/builder" + +module RV32M + include SimInfra + extend SimInfra + + Instruction(:mul) { + encoding *format_r(0b0110011, 0b000, 0b0000001) + asm { "mul {rd}, {rs1}, {rs2}" } + code { rd[]= rs1.s * rs2.s } + } + + Instruction(:mulh) { + encoding *format_r(0b0110011, 0b001, 0b0000001) + asm { "mulh {rd}, {rs1}, {rs2}" } + code { rd[]= (rs1.s64 * rs2.s64) >> 32 } + } + + Instruction(:mulhsu) { + encoding *format_r(0b0110011, 0b010, 0b0000001) + asm { "mulhsu {rd}, {rs1}, {rs2}" } + code { rd[]= (rs1.s64 * rs2.u64.s64) >> 32 } + } + + Instruction(:mulhu) { + encoding *format_r(0b0110011, 0b011, 0b0000001) + asm { "mulhu {rd}, {rs1}, {rs2}" } + code { rd[]= (rs1.u64 * rs2.u64) >> 32 } + } + + Instruction(:div) { + encoding *format_r(0b0110011, 0b100, 0b0000001) + asm { "div {rd}, {rs1}, {rs2}" } + code { rd[]= select(rs2 != 0, rs1.s / select(rs2 != 0, rs2.s, 1), 0xffffffff) } + } + + Instruction(:divu) { + encoding *format_r(0b0110011, 0b101, 0b0000001) + asm { "divu {rd}, {rs1}, {rs2}" } + code { rd[]= select(rs2 != 0, rs1.u / select(rs2 != 0, rs2.u, 1), 0xffffffff) } + } + + Instruction(:rem) { + encoding *format_r(0b0110011, 0b110, 0b0000001) + asm { "rem {rd}, {rs1}, {rs2}" } + code { rd[]= rs1.s % rs2.s } + } + + Instruction(:remu) { + encoding *format_r(0b0110011, 0b111, 0b0000001) + asm { "remu {rd}, {rs1}, {rs2}" } + code { rd[]= rs1.u % rs2.u } + } +end diff --git a/lib/Target/RISC-V/encoding.rb b/lib/Target/RISC-V/encoding.rb index 5ff0310..ba77af3 100644 --- a/lib/Target/RISC-V/encoding.rb +++ b/lib/Target/RISC-V/encoding.rb @@ -1,32 +1,41 @@ require_relative "../../ADL/base" +require_relative "../../ADL/builder" module SimInfra - def u_imm(imm) - return imm, :s32, "let :#{imm}, [:op], :s32, f_#{imm}.s << 12" + XReg = Operand(:XReg, :r32) do + map { |name| let name, :XRegs, [:op], :r32, send("f_#{name}") } end - def i_imm(imm) - return imm, :s32, "let :#{imm}, [:op], :s32, f_#{imm}.s32" + IImm = Operand(:IImm, :s32) do + map { |name| let name, [:op], :s32, send("f_#{name}").s32 } end - def is_imm(imm) - return imm, :s32, "let :#{imm}, [:op], :s32, f_imm4_0" + UImm = Operand(:UImm, :s32) do + map { |name| let name, [:op], :s32, (send("f_#{name}").s << 12) } end - def j_imm(imm) - return imm, :s32, "let :#{imm}, [:op], :s32, (f_imm20.b21 << 20 | f_imm19_12.b21 << 12 | f_imm11.b21 << 11 | f_imm10_1.b21 << 1).s32" + IsImm = Operand(:IsImm, :s32) do + map { |name| let name, [:op], :s32, f_imm4_0 } end - def b_imm(imm) - return imm, :s32, "let :#{imm}, [:op], :s32, (f_imm12.b13 << 12 | f_imm11.b13 << 11 | f_imm10_5.b13 << 5 | f_imm4_1.b13 << 1).s32" + JImm = Operand(:JImm, :s32) do + map { |name| + let name, [:op], :s32, + (f_imm20.b21 << 20 | f_imm19_12.b21 << 12 | f_imm11.b21 << 11 | f_imm10_1.b21 << 1).s32 + } end - def s_imm(imm) - return imm, :s32, "let :#{imm}, [:op], :s32, (f_imm11_5.b12 << 5 | f_imm4_0.b12).s32" + BImm = Operand(:BImm, :s32) do + map { |name| + let name, [:op], :s32, + (f_imm12.b13 << 12 | f_imm11.b13 << 11 | f_imm10_5.b13 << 5 | f_imm4_1.b13 << 1).s32 + } end - def xreg(name) - return name, :r32, "let :#{name}, :XRegs, [:op], :r32, f_#{name}" + SImm = Operand(:SImm, :s32) do + map { |name| + let name, [:op], :s32, (f_imm11_5.b12 << 5 | f_imm4_0.b12).s32 + } end end @@ -36,7 +45,7 @@ def format_u(opcode) field(:f_opcode, 6, 0, opcode), field(:f_rd, 11, 7), field(:f_imm, 31, 12), - ], xreg(:rd), u_imm(:imm) + ], XReg[:rd], UImm[:imm] end def format_r(opcode, funct3, funct7) @@ -47,7 +56,7 @@ def format_r(opcode, funct3, funct7) field(:f_rs1, 19, 15), field(:f_rs2, 24, 20), field(:f_funct7, 31, 25, funct7), - ], xreg(:rs2), xreg(:rs1), xreg(:rd) + ], XReg[:rs2], XReg[:rs1], XReg[:rd] end def format_i(opcode, funct3) @@ -57,19 +66,18 @@ def format_i(opcode, funct3) field(:f_funct3, 14, 12, funct3), field(:f_rs1, 19, 15), field(:f_imm, 31, 20), - ], i_imm(:imm), xreg(:rs1), xreg(:rd) + ], IImm[:imm], XReg[:rs1], XReg[:rd] end - def format_i_shift(opcode, func3, sopcode) - return :srai, [ + def format_i_shift(opcode, func3, funct7) + return :I, [ field(:f_opcode, 6, 0, opcode), - field(:func3, 14, 12, func3), - field(:f_imm4_0, 24, 20), field(:f_rd, 11, 7), + field(:f_funct3, 14, 12, func3), field(:f_rs1, 19, 15), - field(:f_temp, 26, 25, 0b01), - field(:f_sopcode, 31, 27, sopcode), - ], is_imm(:imm), xreg(:rs1), xreg(:rd) + field(:f_imm11_5, 31, 25, funct7), + field(:f_imm4_0, 24, 20), + ], IsImm[:imm], XReg[:rs1], XReg[:rd] end def format_b(opcode, funct3) @@ -82,7 +90,7 @@ def format_b(opcode, funct3) field(:f_imm10_5, 30, 25), field(:f_imm11, 7, 7), field(:f_imm12, 31, 31), - ], b_imm(:imm), xreg(:rs1), xreg(:rs2) + ], BImm[:imm], XReg[:rs1], XReg[:rs2] end def format_j(opcode) @@ -93,7 +101,7 @@ def format_j(opcode) field(:f_imm19_12, 19, 12), field(:f_imm11, 20, 20), field(:f_imm10_1, 30, 21), - ], j_imm(:imm), xreg(:rd) + ], JImm[:imm], XReg[:rd] end def format_s(opcode, func3) @@ -104,6 +112,6 @@ def format_s(opcode, func3) field(:f_rs1, 19, 15), field(:f_rs2, 24, 20), field(:f_imm11_5, 31, 25), - ], s_imm(:imm), xreg(:rs1), xreg(:rs2) + ], SImm[:imm], XReg[:rs1], XReg[:rs2] end end diff --git a/lib/Utility/helper_cpp.rb b/lib/Utility/helper_cpp.rb index d54f32f..6b1d445 100644 --- a/lib/Utility/helper_cpp.rb +++ b/lib/Utility/helper_cpp.rb @@ -7,15 +7,17 @@ module Utility module HelperCpp module_function - def gen_type(type) + def gen_type(type, signed = false) actual_type = Utility.get_type(type) - cpp_bitsize = actual_type.bitsize % 32 == 0 ? actual_type.bitsize : (actual_type.bitsize / 32 + 1) * 32 - "#{actual_type.typeof == :s ? 'int' : 'uint'}#{cpp_bitsize}_t" + width = actual_type.bitsize + prefix = signed || actual_type.typeof == :s ? "" : "u" + rounded = [1, 8, 16, 32, 64, 128].find { |s| s >= width } || 128 + rounded == 1 ? "bool" : "#{prefix}int#{rounded}_t" end def gen_small_type(type) actual_type = Utility.get_type(type) - cpp_bitsize = actual_type.bitsize % 8 == 0 ? actual_type.bitsize : (actual_type.bitsize / 8 + 1) * 8 + cpp_bitsize = [8, 1 << (actual_type.bitsize - 1).bit_length].max "#{actual_type.typeof == :s ? 'int' : 'uint'}#{cpp_bitsize}_t" end end diff --git a/lib/Utility/lira_utils.rb b/lib/Utility/lira_utils.rb new file mode 100644 index 0000000..ce53b1a --- /dev/null +++ b/lib/Utility/lira_utils.rb @@ -0,0 +1,99 @@ +# lira/lira_utils.rb + +require_relative '../lira/ir_ops' + +module ADLToLiraUtils + + SPECIAL_REGS = %w[pc].freeze + + def self.convert_type(adl_type) + return 32 if adl_type.nil? + case adl_type.to_s + when /^b(\d+)$/ then $1.to_i + when /^u(\d+)$/, /^s(\d+)$/, /^r(\d+)$/ then $1.to_i + else 32 + end + end + + def self.resolve_operand(op, builder, vars, operand_names, arch_builder = nil) + return nil if op.nil? + return vars[op] if vars.key?(op) + + case op + when Integer + builder.const(op, 32) + when SimInfra::Constant + builder.const(op.value, 32) + when SimInfra::Var + resolve_var(op, builder, vars, operand_names, arch_builder) + else + raise "Cannot resolve operand: #{op.inspect}" + end + end + + def self.resolve_var(var, builder, vars, operand_names, arch_builder) + width = convert_type(var.type) + width = 1 if width < 1 + + if (idx = operand_names.index(var.name.to_s)) + return resolve_operand_var(var, idx, width, builder, vars, arch_builder) + end + if SPECIAL_REGS.include?(var.name.to_s) + return resolve_special_reg(var, builder, vars, arch_builder) + end + val = builder.seq.new_temp(width) + vars[var] = val + val + end + + def self.resolve_operand_var(var, idx, width, builder, vars, arch_builder) + input_val = builder.input(idx, width) + vars[var] = input_val + return input_val unless var.regset + + rf_name = var.regset.to_s + rf = arch_builder.register_files.find { |rf| rf.name == rf_name } + unless rf + warn "Register file '#{rf_name}' not found, skipping statement" + return nil + end + read_val = builder.seq.new_temp(width) + builder.read(rf, input_val) + vars[var] = read_val + read_val + end + + def self.resolve_special_reg(var, builder, vars, arch_builder) + ef = find_env_func(arch_builder, "getPC", [], [32]) + unless ef + warn "Environment function 'getPC' not registered" + return nil + end + val = builder.env(ef, [])[0] + vars[var] = val + val + end + + def self.find_env_func(arch_builder, name, input_types, output_types) + arch_builder.environment_functions.find do |ef| + ef.name == name && ef.inputs == input_types && ef.outputs == output_types + end + end + + def self.extract_field(builder, enc, field, dest_type) + low_bit = field.to + high_bit = field.from + width = high_bit - low_bit + 1 + shifted = builder.lsr(enc, builder.const(low_bit, 32)) + val = builder.extract_low(shifted, width) + dest_width = convert_type(dest_type) + if width != dest_width + if dest_type.to_s.start_with?('s') + val = builder.extend_sign(val, dest_width) + else + val = builder.extend_zero(val, dest_width) + end + end + val + end +end diff --git a/lib/ir_gen.rb b/lib/ir_gen.rb index cde48e4..1a66c17 100644 --- a/lib/ir_gen.rb +++ b/lib/ir_gen.rb @@ -4,8 +4,7 @@ require 'ADL/base' require 'ADL/builder' require 'Target/RISC-V/32I' - -require 'yaml' +require 'Target/RISC-V/32M' yaml_data = SimInfra.serialize File.write('IR.yaml', yaml_data) diff --git a/lib/lira/arch.rb b/lib/lira/arch.rb new file mode 100644 index 0000000..8775d25 --- /dev/null +++ b/lib/lira/arch.rb @@ -0,0 +1,434 @@ +# lira/arch.rb +require 'set' + +module Lira + class Component + attr_accessor :name, :attributes + + def initialize(name, attributes = []) + @name = name + @attributes = attributes + end + + def to_h + { name: name, attributes: attributes } + end + + def self.from_h(hash) + new(hash[:name] || hash['name'], hash[:attributes] || hash['attributes'] || []) + end + + def ==(other) + other.is_a?(Component) && name == other.name && attributes == other.attributes + end + end + + class Snippet + attr_accessor :name, :seq + + def initialize(name, seq) + @name = name + @seq = seq + end + + def to_h + { name: name, seq: seq } + end + + def self.from_h(hash) + seq = StatementSeq.from_h(hash[:seq] || hash['seq']) + new(hash[:name] || hash['name'], seq) + end + + def ==(other) + other.is_a?(Snippet) && name == other.name && seq == other.seq + end + end + + class Operation < Component + attr_accessor :inputs, :outputs, :semantic_base, :semantic_func, :semantic_func_128, :semantic_table + + def initialize(name, attributes, inputs, outputs, + semantic_base: nil, semantic_func: nil, semantic_func_128: nil, semantic_table: nil) + super(name, attributes) + @inputs = inputs + @outputs = outputs + @semantic_base = semantic_base + @semantic_func = semantic_func + @semantic_func_128 = semantic_func_128 + @semantic_table = semantic_table + end + + def to_h + super.merge( + inputs: inputs, + outputs: outputs, + semantic_base: semantic_base, + semantic_func: semantic_func, + semantic_func_128: semantic_func_128, + semantic_table: semantic_table + ) + end + + def self.from_h(hash) + name = hash[:name] || hash['name'] + attributes = hash[:attributes] || hash['attributes'] || [] + inputs = hash[:inputs] || hash['inputs'] + outputs = hash[:outputs] || hash['outputs'] + + semantic_base = hash[:semantic_base] || hash['semantic_base'] + semantic_base = nil if semantic_base == {} + semantic_func = hash[:semantic_func] || hash['semantic_func'] + semantic_func_128 = hash[:semantic_func_128] || hash['semantic_func_128'] + semantic_func_128 = nil if semantic_func_128 == {} + semantic_table = hash[:semantic_table] || hash['semantic_table'] + semantic_table = nil if semantic_table == {} + + new(name, attributes, inputs, outputs, + semantic_base: semantic_base, + semantic_func: semantic_func, + semantic_func_128: semantic_func_128, + semantic_table: semantic_table) + end + + def ==(other) + super(other) && + other.is_a?(Operation) && + inputs == other.inputs && + outputs == other.outputs && + semantic_base == other.semantic_base && + semantic_func == other.semantic_func && + semantic_func_128 == other.semantic_func_128 && + semantic_table == other.semantic_table + end + end + + class Register < Component + def initialize(name, attributes = []) + super(name, attributes) + end + + def to_h + super.merge() + end + + def self.from_h(hash) + name = hash[:name] || hash['name'] + attributes = hash[:attributes] || hash['attributes'] || [] + new(name, attributes) + end + + def ==(other) + super(other) && other.is_a?(Register) + end + end + + class RegisterFile < Component + attr_accessor :reg_size, :regs + + def initialize(name, attributes, reg_size, regs) + super(name, attributes) + @reg_size = reg_size + @regs = regs + end + + def reg_names + @regs.map(&:name) + end + + def regs_num + @regs.length + end + + def to_h + super.merge(reg_size: reg_size.to_h, regs: regs.map(&:to_h)) + end + + def self.from_h(hash) + name = hash[:name] || hash['name'] + attributes = hash[:attributes] || hash['attributes'] || [] + reg_size = Shape.from_h(hash[:reg_size] || hash['reg_size']) + regs_data = hash[:regs] || hash['regs'] || [] + regs = regs_data.map { |r| Register.from_h(r) } + new(name, attributes, reg_size, regs) + end + + def ==(other) + super(other) && + other.is_a?(RegisterFile) && + reg_size == other.reg_size && + regs == other.regs + end + end + + + class EnvironmentFunction < Component + attr_accessor :inputs, :outputs + + def initialize(name, attributes, inputs, outputs) + super(name, attributes) + @inputs = inputs + @outputs = outputs + end + + def to_h + super.merge(inputs: inputs, outputs: outputs) + end + + def self.from_h(hash) + name = hash[:name] || hash['name'] + attributes = hash[:attributes] || hash['attributes'] || [] + inputs = hash[:inputs] || hash['inputs'] + outputs = hash[:outputs] || hash['outputs'] + new(name, attributes, inputs, outputs) + end + + def ==(other) + super(other) && + other.is_a?(EnvironmentFunction) && + inputs == other.inputs && + outputs == other.outputs + end + end + + class SystemRegisterField < Component + attr_accessor :lsb, :msb + + def initialize(name, attributes, lsb, msb) + super(name, attributes) + @lsb = lsb + @msb = msb + end + + def to_h + super.merge(lsb: lsb, msb: msb) + end + + def self.from_h(hash) + name = hash[:name] || hash['name'] + attributes = hash[:attributes] || hash['attributes'] || [] + lsb = hash[:lsb] || hash['lsb'] + msb = hash[:msb] || hash['msb'] + new(name, attributes, lsb, msb) + end + + def ==(other) + super(other) && + other.is_a?(SystemRegisterField) && + lsb == other.lsb && + msb == other.msb + end + end + + class SystemRegister < Component + attr_accessor :size, :fields + + def initialize(name, attributes, size, fields) + super(name, attributes) + @size = size + @fields = fields + end + + def to_h + super.merge(size: size, fields: fields.map(&:to_h)) + end + + def self.from_h(hash) + name = hash[:name] || hash['name'] + attributes = hash[:attributes] || hash['attributes'] || [] + size = hash[:size] || hash['size'] + fields_data = hash[:fields] || hash['fields'] + fields = fields_data.map { |f| SystemRegisterField.from_h(f) } + new(name, attributes, size, fields) + end + + def ==(other) + super(other) && + other.is_a?(SystemRegister) && + size == other.size && + fields == other.fields + end + end + + class TableInt < Component + attr_accessor :values + + def initialize(name, attributes, values) + super(name, attributes) + @values = values + end + + def to_h + super.merge(values: values) + end + + def self.from_h(hash) + name = hash[:name] || hash['name'] + attributes = hash[:attributes] || hash['attributes'] || [] + values = hash[:values] || hash['values'] + new(name, attributes, values) + end + + def ==(other) + super(other) && + other.is_a?(TableInt) && + values == other.values + end + end + + class InstructionEncoding + attr_accessor :encoded_size, :const_encoding_part, :const_mask, :decode, :encode, + :constraint_decode, :constraint_encode + + def initialize(encoded_size, const_encoding_part, const_mask, decode, encode, constraint_decode, constraint_encode) + @encoded_size = encoded_size + @const_encoding_part = const_encoding_part + @const_mask = const_mask + @decode = decode + @encode = encode + @constraint_decode = constraint_decode + @constraint_encode = constraint_encode + end + + def to_h + { + encoded_size: encoded_size, + const_encoding_part: const_encoding_part, + const_mask: const_mask, + decode: decode, + encode: encode, + constraint_decode: constraint_decode, + constraint_encode: constraint_encode + } + end + + def self.from_h(hash) + new(hash[:encoded_size] || hash['encoded_size'], + hash[:const_encoding_part] || hash['const_encoding_part'], + hash[:const_mask] || hash['const_mask'], + hash[:decode] || hash['decode'], + hash[:encode] || hash['encode'], + hash[:constraint_decode] || hash['constraint_decode'], + hash[:constraint_encode] || hash['constraint_encode']) + end + + def ==(other) + other.is_a?(InstructionEncoding) && + encoded_size == other.encoded_size && + const_mask == other.const_mask && + const_encoding_part == other.const_encoding_part && + decode == other.decode && + encode == other.encode && + constraint_decode == other.constraint_decode && + constraint_encode == other.constraint_encode + end + end + + class Instruction < Component + attr_accessor :operand_sizes, :operand_names, :encoding, :semantic + + def initialize(name, attributes, operand_sizes, operand_names, encoding, semantic) + super(name, attributes) + @operand_sizes = operand_sizes + @operand_names = operand_names + @encoding = encoding + @semantic = semantic + end + + def to_h + super.merge( + operand_sizes: operand_sizes, + operand_names: operand_names, + encoding: encoding.to_h, + semantic: semantic + ) + end + + def self.from_h(hash) + name = hash[:name] || hash['name'] + attributes = hash[:attributes] || hash['attributes'] || [] + operand_sizes = hash[:operand_sizes] || hash['operand_sizes'] + operand_names = hash[:operand_names] || hash['operand_names'] + encoding = InstructionEncoding.from_h(hash[:encoding] || hash['encoding']) + semantic = StatementSeq.from_h(hash[:semantic] || hash['semantic']) + new(name, attributes, operand_sizes, operand_names, encoding, semantic) + end + + def ==(other) + super(other) && + other.is_a?(Instruction) && + operand_sizes == other.operand_sizes && + operand_names == other.operand_names && + encoding == other.encoding && + semantic == other.semantic + end + end + + class Arch < Component + attr_accessor :register_files, :system_registers, :environment_functions, + :tables_int, :operations, :snippets, :instructions + + def initialize(name, attributes, + register_files: [], + system_registers: [], + environment_functions: [], + tables_int: [], + operations: [], + snippets: [], + instructions: []) + super(name, attributes) + @register_files = register_files + @system_registers = system_registers + @environment_functions = environment_functions + @tables_int = tables_int + @operations = operations + @snippets = snippets + @instructions = instructions + end + + + def to_h + super.merge( + register_files: register_files.map(&:to_h), + system_registers: system_registers.map(&:to_h), + environment_functions: environment_functions.map(&:to_h), + tables_int: tables_int.map(&:to_h), + operations: operations.map(&:to_h), + snippets: snippets.map(&:to_h), + instructions: instructions.map(&:to_h) + ) + end + + def self.from_h(hash) + name = hash[:name] || hash['name'] + attributes = hash[:attributes] || hash['attributes'] || [] + register_files = (hash[:register_files] || hash['register_files']).map { |rf| RegisterFile.from_h(rf) } + system_registers = (hash[:system_registers] || hash['system_registers']).map { |sr| SystemRegister.from_h(sr) } + environment_functions = (hash[:environment_functions] || hash['environment_functions']).map { |ef| EnvironmentFunction.from_h(ef) } + tables_int = (hash[:tables_int] || hash['tables_int']).map { |ti| TableInt.from_h(ti) } + operations = (hash[:operations] || hash['operations']).map { |op| Operation.from_h(op) } + snippets = (hash[:snippets] || hash['snippets']).map { |sn| Snippet.from_h(sn) } + instructions = (hash[:instructions] || hash['instructions']).map { |ins| Instruction.from_h(ins) } + new(name, attributes, + register_files: register_files, + system_registers: system_registers, + environment_functions: environment_functions, + tables_int: tables_int, + operations: operations, + snippets: snippets, + instructions: instructions) + end + + def ==(other) + super(other) && + other.is_a?(Arch) && + register_files == other.register_files && + system_registers == other.system_registers && + environment_functions == other.environment_functions && + tables_int == other.tables_int && + operations == other.operations && + snippets == other.snippets && + instructions == other.instructions + end + end +end diff --git a/lib/lira/arch_ser_yaml.rb b/lib/lira/arch_ser_yaml.rb new file mode 100644 index 0000000..ba68c65 --- /dev/null +++ b/lib/lira/arch_ser_yaml.rb @@ -0,0 +1,60 @@ +# lira/arch_ser_yaml.rb +require 'yaml' +require_relative 'ir' +require_relative 'arch' + +module Lira + module ArchSerYaml + module_function + + def to_serializable(obj) + case obj + when nil + nil + when StatementSeq + IrSerTxt.serialize_statement_seq(obj) + when Array + obj.map { |item| to_serializable(item) } + when Hash + obj.transform_keys(&:to_s).transform_values { |v| to_serializable(v) } + else + if obj.respond_to?(:to_h) + obj.to_h.transform_keys(&:to_s).transform_values { |v| to_serializable(v) } + else + obj + end + end + end + + def from_serializable(klass, data, item_class = nil) + if klass == Array + if item_class + return data.map { |elem| from_serializable(item_class, elem) } + else + return data.map { |elem| from_serializable(Object, elem) } + end + end + + if klass == StatementSeq + return IrSerTxt.deserialize_statement_seq(data) + end + + if klass.respond_to?(:from_h) + transformed = data.transform_keys(&:to_sym).transform_values { |v| from_serializable(Object, v) } + klass.from_h(transformed) + else + data + end + end + + def write_arch(arch, filepath) + data = to_serializable(arch) + File.write(filepath, YAML.dump(data)) + end + + def read_arch(filepath) + data = YAML.load_file(filepath) + from_serializable(Arch, data) + end + end +end diff --git a/lib/lira/arch_utils.rb b/lib/lira/arch_utils.rb new file mode 100644 index 0000000..3484a42 --- /dev/null +++ b/lib/lira/arch_utils.rb @@ -0,0 +1,18 @@ +# lira/arch_utils.rb +require_relative 'arch' + +module Lira + ArchIndex = Struct.new(:rf, :sr, :env, :tables, :op, :snippet, :instr) + + def self.build_arch_index(arch) + ArchIndex.new( + arch.register_files.to_h { |rf| [rf.name, rf] }, + arch.system_registers.to_h { |sr| [sr.name, sr] }, + arch.environment_functions.to_h { |ef| [ef.name, ef] }, + arch.tables_int.to_h { |ti| [ti.name, ti] }, + arch.operations.to_h { |op| [op.name, op] }, + arch.snippets.to_h { |sn| [sn.name, sn] }, + arch.instructions.to_h { |ins| [ins.name, ins] } + ) + end +end diff --git a/lib/lira/ir.rb b/lib/lira/ir.rb new file mode 100644 index 0000000..5d83533 --- /dev/null +++ b/lib/lira/ir.rb @@ -0,0 +1,147 @@ +# lira/ir.rb +require 'set' + +module Lira + class Shape + attr_accessor :lanes_base, :lanes_mult + + def initialize(lanes_base, lanes_mult) + @lanes_base = lanes_base + @lanes_mult = lanes_mult + end + + def to_h + { lanes_base: lanes_base, lanes_mult: lanes_mult } + end + + def self.from_h(hash) + lanes_base = hash[:lanes_base] || hash['lanes_base'] + lanes_mult = hash[:lanes_mult] || hash['lanes_mult'] + lanes_mult = nil if lanes_mult == {} + new(lanes_base, lanes_mult) + end + + def ==(other) + other.is_a?(Shape) && lanes_base == other.lanes_base && lanes_mult == other.lanes_mult + end + end + + class Statement + attr_accessor :shape, :outputs, :outputs_types, :kind, :specifier, :inputs + + def initialize(shape, outputs, outputs_types, kind, specifier, inputs) + @shape = shape + @outputs = outputs + @outputs_types = outputs_types + @kind = kind + @specifier = specifier + @inputs = inputs + end + + def to_h + { + shape: shape.to_h, + outputs: outputs, + outputs_types: outputs_types, + kind: kind, + specifier: specifier, + inputs: inputs + } + end + + def self.from_h(hash) + shape = Shape.from_h(hash[:shape] || hash['shape']) + new(shape, + hash[:outputs] || hash['outputs'], + hash[:outputs_types] || hash['outputs_types'], + hash[:kind] || hash['kind'], + hash[:specifier] || hash['specifier'], + hash[:inputs] || hash['inputs']) + end + + def ==(other) + other.is_a?(Statement) && + shape == other.shape && + outputs == other.outputs && + outputs_types == other.outputs_types && + kind == other.kind && + specifier == other.specifier && + inputs == other.inputs + end + + def input(id, seq) + target = @inputs[id] + seq.stmts.each do |stmt| + return stmt if stmt.outputs.include?(target) + end + raise "input #{target} not found" + end + end + + class StatementSeq + attr_accessor :stmts + + def initialize(stmts) + @stmts = stmts + end + + def to_h + { stmts: stmts.map(&:to_h) } + end + + def self.from_h(data) + if data.is_a?(String) + IrSerTxt.deserialize_statement_seq(data) + else + stmts_data = data[:stmts] || data['stmts'] + stmts = stmts_data.map { |stmt_h| Statement.from_h(stmt_h) } + new(stmts) + end + end + + def ==(other) + other.is_a?(StatementSeq) && stmts == other.stmts + end + end + + module IrSerTxt + def self.serialize_shape(shape) + "#{shape.lanes_base}#{shape.lanes_mult || ''}" + end + + def self.deserialize_shape(s) + m = s.match(/\A(\d+)(.*)\z/) + raise "invalid shape: #{s}" unless m + Shape.new(m[1].to_i, m[2].empty? ? nil : m[2]) + end + + def self.serialize_statement(stmt) + shape_str = serialize_shape(stmt.shape) + out_parts = stmt.outputs_types.zip(stmt.outputs).flat_map { |typ, name| [typ.to_s, name] } + parts = [shape_str] + out_parts + ['=', stmt.kind, stmt.specifier] + stmt.inputs + parts.join(' ') + end + + def self.deserialize_statement(s) + m = s.match(/\A(\w+)\s+(.*?)\s*=\s*(\w+)\s+(\S+)\s*(.*)\z/) + raise "invalid statement: #{s}" unless m + shape_str, outputs_str, kind, specifier, inputs_str = m.captures + shape = deserialize_shape(shape_str) + pairs = outputs_str.split + outputs_types = (0...pairs.length).step(2).map { |i| pairs[i].to_i } + outputs = (0...pairs.length).step(2).map { |i| pairs[i+1] } + inputs = inputs_str.empty? ? [] : inputs_str.split + Statement.new(shape, outputs, outputs_types, kind, specifier, inputs) + end + + def self.serialize_statement_seq(seq) + seq.stmts.map { |s| serialize_statement(s) + ";\n" }.join + end + + def self.deserialize_statement_seq(s) + raw_stmts = s.split(';').map(&:strip).reject(&:empty?) + stmts = raw_stmts.map { |raw| deserialize_statement(raw) } + StatementSeq.new(stmts) + end + end +end diff --git a/lib/lira/ir_builder.rb b/lib/lira/ir_builder.rb new file mode 100644 index 0000000..6e8850d --- /dev/null +++ b/lib/lira/ir_builder.rb @@ -0,0 +1,694 @@ +# lira/ir_builder.rb +require_relative 'ir' +require_relative 'arch' +require_relative 'ir_ops' + +module Lira + class Value + attr_accessor :name, :width + + def initialize(name, width = 32) + @name = name + @width = width + @shape = Shape.new(1, nil) + end + + def to_s + name + end + + def inspect + "Value(#{name}, #{width})" + end + + def ==(other) + other.is_a?(Value) && name == other.name && width == other.width + end + end + + class SeqBuilder + attr_reader :stmts, :temp_counter + + def initialize + @stmts = [] + @temp_counter = 0 + end + + def new_temp(width = 32) + @temp_counter += 1 + Value.new("_t#{@temp_counter}", width) + end + + def emit_op(op, inputs, out_bits) + out = new_temp(out_bits) + add_op(op, inputs, [out.name]) + out + end + + def check_width_match(a, b) + raise "width mismatch: #{a.width} != #{b.width}" if a.width != b.width + end + + # ------------------------------------------------------------------ + # NOTE: Building ruby/lira/ir_ops.rb objects + # ------------------------------------------------------------------ + def add(a, b) + check_width_match(a, b) + emit_op(Add.new(a.width), [a.name, b.name], a.width) + end + + def sub(a, b) + check_width_match(a, b) + emit_op(Sub.new(a.width), [a.name, b.name], a.width) + end + + def mul(a, b) + check_width_match(a, b) + emit_op(Mul.new(a.width), [a.name, b.name], a.width) + end + + def and_(a, b) + check_width_match(a, b) + emit_op(And.new(a.width), [a.name, b.name], a.width) + end + + def orr(a, b) + check_width_match(a, b) + emit_op(Orr.new(a.width), [a.name, b.name], a.width) + end + + def xor(a, b) + check_width_match(a, b) + emit_op(Xor.new(a.width), [a.name, b.name], a.width) + end + + def lsl(a, b) + check_width_match(a, b) + emit_op(Lsl.new(a.width), [a.name, b.name], a.width) + end + + def lsr(a, b) + check_width_match(a, b) + emit_op(Lsr.new(a.width), [a.name, b.name], a.width) + end + + def asr(a, b) + check_width_match(a, b) + emit_op(Asr.new(a.width), [a.name, b.name], a.width) + end + + def slt(a, b) + check_width_match(a, b) + emit_op(Slt.new(a.width), [a.name, b.name], 1) + end + + def sle(a, b) + check_width_match(a, b) + emit_op(Sle.new(a.width), [a.name, b.name], 1) + end + + def sgt(a, b) + check_width_match(a, b) + emit_op(Sgt.new(a.width), [a.name, b.name], 1) + end + + def sge(a, b) + check_width_match(a, b) + emit_op(Sge.new(a.width), [a.name, b.name], 1) + end + + def ult(a, b) + check_width_match(a, b) + emit_op(Ult.new(a.width), [a.name, b.name], 1) + end + + def ule(a, b) + check_width_match(a, b) + emit_op(Ule.new(a.width), [a.name, b.name], 1) + end + + def ugt(a, b) + check_width_match(a, b) + emit_op(Ugt.new(a.width), [a.name, b.name], 1) + end + + def uge(a, b) + check_width_match(a, b) + emit_op(Uge.new(a.width), [a.name, b.name], 1) + end + + def eq(a, b) + check_width_match(a, b) + emit_op(Eq.new(a.width), [a.name, b.name], 1) + end + + def ne(a, b) + check_width_match(a, b) + emit_op(Ne.new(a.width), [a.name, b.name], 1) + end + + def rem_u(a, b) + check_width_match(a, b) + emit_op(RemU.new(a.width), [a.name, b.name], a.width) + end + + def rem_s(a, b) + check_width_match(a, b) + emit_op(RemS.new(a.width), [a.name, b.name], a.width) + end + + def ror(a, b) + check_width_match(a, b) + emit_op(Ror.new(a.width), [a.name, b.name], a.width) + end + + def rol(a, b) + check_width_match(a, b) + emit_op(Rol.new(a.width), [a.name, b.name], a.width) + end + + def add_overflow(a, b) + check_width_match(a, b) + emit_op(AddOverflow.new(a.width), [a.name, b.name], 1) + end + + def sub_overflow(a, b) + check_width_match(a, b) + emit_op(SubOverflow.new(a.width), [a.name, b.name], 1) + end + + def not_(a) + emit_op(Not.new(a.width), [a.name], a.width) + end + + def neg(a) + emit_op(Neg.new(a.width), [a.name], a.width) + end + + def popcnt(a) + emit_op(Popcnt.new(a.width), [a.name], a.width) + end + + def ctz(a) + emit_op(Ctz.new(a.width), [a.name], a.width) + end + + def clz(a) + emit_op(Clz.new(a.width), [a.name], a.width) + end + + def reverse(a) + emit_op(Reverse.new(a.width), [a.name], a.width) + end + + def extend_sign(a, to_width) + raise "extend_sign: input width #{a.width} >= output width #{to_width}" if a.width >= to_width + emit_op(ExtendSign.new(a.width, to_width), [a.name], to_width) + end + + def extend_zero(a, to_width) + raise "extend_zero: input width #{a.width} >= output width #{to_width}" if a.width >= to_width + emit_op(ExtendZero.new(a.width, to_width), [a.name], to_width) + end + + def extract_low(a, out_width) + raise "extract_low: output width #{out_width} > input width #{a.width}" if out_width > a.width + emit_op(ExtractLow.new(a.width, out_width), [a.name], out_width) + end + + def div_u(a, b, default) + check_width_match(a, b) + raise "div_u: default width mismatch" if a.width != default.width + emit_op(DivU.new(a.width), [a.name, b.name, default.name], a.width) + end + + def div_s(a, b, default) + check_width_match(a, b) + raise "div_s: default width mismatch" if a.width != default.width + emit_op(DivS.new(a.width), [a.name, b.name, default.name], a.width) + end + + def select(cond, true_val, false_val) + raise "select: condition must be 1-bit" unless cond.width == 1 + raise "select: true/false widths mismatch" unless true_val.width == false_val.width + emit_op( + Select.new(true_val.width), + [cond.name, true_val.name, false_val.name], + true_val.width + ) + end + + + # ------------------------------------------------------------------ + # NOTE: Building ruby/lira/ir_std.rb objects + # ------------------------------------------------------------------ + def read(rf, rsi, shape = Shape.new(1, nil)) + width = rf.reg_size.lanes_base + out = new_temp(width) + stmt = Statement.new(shape, [out.name], [width], 'read', rf.name, [rsi.name]) + @stmts << stmt + out + end + + def write(rf, rsi, value, shape = Shape.new(1, nil)) + stmt = Statement.new(shape, [], [], 'write', rf.name, [rsi.name, value.name]) + @stmts << stmt + end + + def const(value, width = 32) + out = new_temp(width) + stmt = Statement.new(Shape.new(1, nil), [out.name], [width], 'const', value.to_s, []) + @stmts << stmt + out + end + + def dyn_const(name, width = 32) + out = new_temp(width) + stmt = Statement.new(Shape.new(1, nil), [out.name], [width], 'dyn_const', name, []) + @stmts << stmt + out + end + + def env(env_func, inputs) + outputs = env_func.outputs.map { |w| new_temp(w) } + stmt = Statement.new( + Shape.new(1, nil), + outputs.map(&:name), + env_func.outputs, + 'env', + env_func.name, + inputs.map(&:name) + ) + @stmts << stmt + outputs + end + + def cond_env(env_func, cond, inputs, on_false) + outputs = env_func.outputs.map { |w| new_temp(w) } + all_inputs = [cond.name] + inputs.map(&:name) + on_false.map(&:name) + stmt = Statement.new( + Shape.new(1, nil), + outputs.map(&:name), + env_func.outputs, + 'cond_env', + env_func.name, + all_inputs + ) + @stmts << stmt + outputs + end + + def input(idx, width = 32) + out = new_temp(width) + stmt = Statement.new(Shape.new(1, nil), [out.name], [width], 'input', idx.to_s, []) + @stmts << stmt + out + end + + def output(value, idx) + stmt = Statement.new(Shape.new(1, nil), [], [], 'output', idx.to_s, [value.name]) + @stmts << stmt + end + + def add_op(operation, inputs, outputs, shape = Shape.new(1, nil)) + stmt = Statement.new(shape, outputs, operation.outputs, 'op', operation.name, inputs) + @stmts << stmt + end + + def op(operation, inputs) + raise "Operation #{operation.name} has #{operation.outputs.size} outputs, use op_multi" if operation.outputs.size != 1 + emit_op(operation, inputs.map(&:name), operation.outputs.first) + end + + def op_multi(operation, inputs) + outputs = operation.outputs.map { |w| new_temp(w) } + add_op(operation, inputs.map(&:name), outputs.map(&:name)) + outputs + end + + def build + StatementSeq.new(@stmts) + end + + def +(other) + @stmts.concat(other.stmts) + @temp_counter = [@temp_counter, other.temp_counter].max + self + end + end + + class BaseBuilder + attr_reader :seq + + def initialize + @seq = SeqBuilder.new + @op_cache = {} + end + + def cache_op(op_class, *args) + op = op_class.new(*args) + @op_cache[op.name] ||= op + end + + def operations_map + @op_cache.each_value.to_h { |op| [op.name, op] } + end + + # ------------------------------------------------------------------ + # NOTE: Building ruby/lira/ir_ops.rb objects + # ------------------------------------------------------------------ + def add(a, b) + cache_op(Add, a.width) + @seq.add(a, b) + end + + def sub(a, b) + cache_op(Sub, a.width) + @seq.sub(a, b) + end + + def mul(a, b) + cache_op(Mul, a.width) + @seq.mul(a, b) + end + + def and_(a, b) + cache_op(And, a.width) + @seq.and_(a, b) + end + + def orr(a, b) + cache_op(Orr, a.width) + @seq.orr(a, b) + end + + def xor(a, b) + cache_op(Xor, a.width) + @seq.xor(a, b) + end + + def lsl(a, b) + cache_op(Lsl, a.width) + @seq.lsl(a, b) + end + + def lsr(a, b) + cache_op(Lsr, a.width) + @seq.lsr(a, b) + end + + def asr(a, b) + cache_op(Asr, a.width) + @seq.asr(a, b) + end + + def slt(a, b) + cache_op(Slt, a.width) + @seq.slt(a, b) + end + + def sle(a, b) + cache_op(Sle, a.width) + @seq.sle(a, b) + end + + def sgt(a, b) + cache_op(Sgt, a.width) + @seq.sgt(a, b) + end + + def sge(a, b) + cache_op(Sge, a.width) + @seq.sge(a, b) + end + + def ult(a, b) + cache_op(Ult, a.width) + @seq.ult(a, b) + end + + def ule(a, b) + cache_op(Ule, a.width) + @seq.ule(a, b) + end + + def ugt(a, b) + cache_op(Ugt, a.width) + @seq.ugt(a, b) + end + + def uge(a, b) + cache_op(Uge, a.width) + @seq.uge(a, b) + end + + def eq(a, b) + cache_op(Eq, a.width) + @seq.eq(a, b) + end + + def ne(a, b) + cache_op(Ne, a.width) + @seq.ne(a, b) + end + + def rem_u(a, b) + cache_op(RemU, a.width) + @seq.rem_u(a, b) + end + + def rem_s(a, b) + cache_op(RemS, a.width) + @seq.rem_s(a, b) + end + + def ror(a, b) + cache_op(Ror, a.width) + @seq.ror(a, b) + end + + def rol(a, b) + cache_op(Rol, a.width) + @seq.rol(a, b) + end + + def add_overflow(a, b) + cache_op(AddOverflow, a.width) + @seq.add_overflow(a, b) + end + + def sub_overflow(a, b) + cache_op(SubOverflow, a.width) + @seq.sub_overflow(a, b) + end + + def not_(a) + cache_op(Not, a.width) + @seq.not_(a) + end + + def neg(a) + cache_op(Neg, a.width) + @seq.neg(a) + end + + def popcnt(a) + cache_op(Popcnt, a.width) + @seq.popcnt(a) + end + + def ctz(a) + cache_op(Ctz, a.width) + @seq.ctz(a) + end + + def clz(a) + cache_op(Clz, a.width) + @seq.clz(a) + end + + def reverse(a) + cache_op(Reverse, a.width) + @seq.reverse(a) + end + + def extend_sign(a, to_width) + cache_op(ExtendSign, a.width, to_width) + @seq.extend_sign(a, to_width) + end + + def extend_zero(a, to_width) + cache_op(ExtendZero, a.width, to_width) + @seq.extend_zero(a, to_width) + end + + def extract_low(a, out_width) + cache_op(ExtractLow, a.width, out_width) + @seq.extract_low(a, out_width) + end + + def div_u(a, b, default) + cache_op(DivU, a.width) + @seq.div_u(a, b, default) + end + + def div_s(a, b, default) + cache_op(DivS, a.width) + @seq.div_s(a, b, default) + end + + def select(cond, true_val, false_val) + cache_op(Select, true_val.width) + @seq.select(cond, true_val, false_val) + end + + # ------------------------------------------------------------------ + # NOTE: Building ruby/lira/ir_std.rb objects + # ------------------------------------------------------------------ + def read(rf, rsi, shape = Shape.new(1, nil)) + @seq.read(rf, rsi, shape) + end + + def write(rf, rsi, value, shape = Shape.new(1, nil)) + @seq.write(rf, rsi, value, shape) + end + + def const(value, width = 32) + @seq.const(value, width) + end + + def dyn_const(name, width = 32) + @seq.dyn_const(name, width) + end + + def env(env_func, inputs) + @seq.env(env_func, inputs) + end + + def cond_env(env_func, cond, inputs, on_false) + @seq.cond_env(env_func, cond, inputs, on_false) + end + + def input(idx, width = 32) + @seq.input(idx, width) + end + + def output(value, idx) + @seq.output(value, idx) + end + + def op(operation, inputs) + @op_cache[operation.name] ||= operation + @seq.op(operation, inputs) + end + + def op_multi(operation, inputs) + @seq.op_multi(operation, inputs) + end + end + + class SnippetBuilder < BaseBuilder + attr_reader :name + + def initialize(name) + super() + @name = name + end + + def build + Snippet.new(@name, @seq.build) + end + end + + class InstructionBuilder < BaseBuilder + attr_reader :name, :operand_sizes, :operand_names, :encoding + + def initialize(name, operand_sizes, operand_names, encoding) + super() + @name = name + @operand_sizes = operand_sizes + @operand_names = operand_names + @encoding = encoding + end + + def add_input_operand(idx, width = nil) + w = width || (@operand_sizes[idx] if idx < @operand_sizes.size) || 32 + input(idx, w) + end + + def build + Instruction.new( + @name, [], + @operand_sizes, @operand_names, + @encoding, + @seq.build + ) + end + end + + class ArchBuilder + attr_reader :name, :attributes, :register_files, :system_registers, + :environment_functions, :tables_int, :operations, + :snippets, :instructions + + def initialize(name, attributes = []) + @name = name + @attributes = attributes + @register_files = [] + @system_registers = [] + @environment_functions = [] + @tables_int = [] + @operations = [] + @snippets = [] + @instructions = [] + end + + def add_register_file(rf) + @register_files << rf + self + end + + def add_system_register(sr) + @system_registers << sr + self + end + + def add_env_func(env) + @environment_functions << env + self + end + + def add_table_int(table) + @tables_int << table + self + end + + def add_operation(op) + @operations << op + self + end + + def add_snippet(snippet) + @snippets << snippet + self + end + + def add_instruction(instr) + @instructions << instr + self + end + + def build + Arch.new( + @name, @attributes, + register_files: @register_files, + system_registers: @system_registers, + environment_functions: @environment_functions, + tables_int: @tables_int, + operations: @operations, + snippets: @snippets, + instructions: @instructions + ) + end + end +end diff --git a/lib/lira/ir_ops.rb b/lib/lira/ir_ops.rb new file mode 100644 index 0000000..1afc94e --- /dev/null +++ b/lib/lira/ir_ops.rb @@ -0,0 +1,301 @@ +# lira/ir_ops.rb +require_relative 'arch' + +module Lira + class TypeCheckError < StandardError; end + + module BaseOp + NOT = :not + NEG = :neg + ADD = :add + SUB = :sub + MUL = :mul + AND = :and + ORR = :orr + XOR = :xor + LSL = :lsl + LSR = :lsr + ASR = :asr + EQ = :eq + NE = :ne + SLT = :slt + SLE = :sle + SGT = :sgt + SGE = :sge + ULT = :ult + ULE = :ule + UGT = :ugt + UGE = :uge + DIV_U = :div_u + DIV_S = :div_s + REM_U = :rem_u + REM_S = :rem_s + ROR = :ror + ROL = :rol + ADD_OVERFLOW = :add_overflow + SUB_OVERFLOW = :sub_overflow + SELECT = :select + EXTEND_SIGN = :extend_sign + EXTEND_ZERO = :extend_zero + EXTRACT_LOW = :extract_low + POPCNT = :popcnt + CTZ = :ctz + CLZ = :clz + REVERSE = :reverse + end + + class UnaryOp < Operation + def initialize(out_bits, semantic_base, name: nil) + name ||= "#{semantic_base}_#{out_bits}" + super(name, [], [out_bits], [out_bits], + semantic_base: semantic_base, semantic_func: nil, semantic_table: nil) + check_signature + end + + def check_signature + raise TypeCheckError, 'input width must be positive' unless inputs[0] > 0 + raise TypeCheckError, 'output width must be positive' unless outputs[0] > 0 + raise TypeCheckError, 'input != output' unless inputs[0] == outputs[0] + end + end + + class BinaryOp < Operation + def initialize(bits, semantic_base, name: nil) + name ||= "#{semantic_base}_#{bits}" + super(name, [], [bits, bits], [bits], + semantic_base: semantic_base, semantic_func: nil, semantic_table: nil) + check_signature + end + + def check_signature + raise TypeCheckError, 'input[0] must be positive' unless inputs[0] > 0 + raise TypeCheckError, 'input[1] must be positive' unless inputs[1] > 0 + raise TypeCheckError, 'output must be positive' unless outputs[0] > 0 + unless inputs[0] == inputs[1] && inputs[0] == outputs[0] + raise TypeCheckError, "mismatched widths: #{inputs} -> #{outputs[0]}" + end + end + end + + class CmpOp < Operation + def initialize(bits, semantic_base, out_bits = 1, name: nil) + name ||= "#{semantic_base}_#{bits}" + super(name, [], [bits, bits], [out_bits], + semantic_base: semantic_base, semantic_func: nil, semantic_table: nil) + check_signature + end + + def check_signature + raise TypeCheckError, 'input[0] must be positive' unless inputs[0] > 0 + raise TypeCheckError, 'input[1] must be positive' unless inputs[1] > 0 + raise TypeCheckError, 'output must be positive' unless outputs[0] > 0 + raise TypeCheckError, 'input widths differ' unless inputs[0] == inputs[1] + end + end + + class TernaryOp < Operation + def initialize(bits, semantic_base, name: nil) + name ||= "#{semantic_base}_#{bits}" + super(name, [], [bits, bits, bits], [bits], + semantic_base: semantic_base, semantic_func: nil, semantic_table: nil) + check_signature + end + + def check_signature + raise TypeCheckError, 'input[0] must be positive' unless inputs[0] > 0 + raise TypeCheckError, 'input[1] must be positive' unless inputs[1] > 0 + raise TypeCheckError, 'input[2] must be positive' unless inputs[2] > 0 + raise TypeCheckError, 'output must be positive' unless outputs[0] > 0 + unless inputs[0] == inputs[1] && inputs[0] == inputs[2] && inputs[0] == outputs[0] + raise TypeCheckError, "mismatched widths: #{inputs} -> #{outputs[0]}" + end + end + end + + class ExtendOp < Operation + def initialize(in_bits, out_bits, semantic_base, name: nil) + name ||= "#{semantic_base}_#{in_bits}_to_#{out_bits}" + super(name, [], [in_bits], [out_bits], + semantic_base: semantic_base, semantic_func: nil, semantic_table: nil) + check_signature + end + + def check_signature + raise TypeCheckError, 'input width must be positive' unless inputs[0] > 0 + raise TypeCheckError, 'output width must be positive' unless outputs[0] > 0 + raise TypeCheckError, 'input >= output' unless inputs[0] < outputs[0] + end + end + + class ExtractLowOp < Operation + def initialize(in_bits, out_bits, semantic_base, name: nil) + name ||= "#{semantic_base}_#{in_bits}_to_#{out_bits}" + super(name, [], [in_bits], [out_bits], + semantic_base: semantic_base, semantic_func: nil, semantic_table: nil) + check_signature + end + + def check_signature + raise TypeCheckError, 'input width must be positive' unless inputs[0] > 0 + raise TypeCheckError, 'output width must be positive' unless outputs[0] > 0 + raise TypeCheckError, 'output > input' unless outputs[0] <= inputs[0] + end + end + + class Not < UnaryOp + def initialize(bits); super(bits, BaseOp::NOT); end + end + + class Neg < UnaryOp + def initialize(bits); super(bits, BaseOp::NEG); end + end + + class Add < BinaryOp + def initialize(bits); super(bits, BaseOp::ADD); end + end + + class Sub < BinaryOp + def initialize(bits); super(bits, BaseOp::SUB); end + end + + class Mul < BinaryOp + def initialize(bits); super(bits, BaseOp::MUL); end + end + + class And < BinaryOp + def initialize(bits); super(bits, BaseOp::AND); end + end + + class Orr < BinaryOp + def initialize(bits); super(bits, BaseOp::ORR); end + end + + class Xor < BinaryOp + def initialize(bits); super(bits, BaseOp::XOR); end + end + + class Lsl < BinaryOp + def initialize(bits); super(bits, BaseOp::LSL); end + end + + class Lsr < BinaryOp + def initialize(bits); super(bits, BaseOp::LSR); end + end + + class Asr < BinaryOp + def initialize(bits); super(bits, BaseOp::ASR); end + end + + class Eq < CmpOp + def initialize(bits); super(bits, BaseOp::EQ); end + end + + class Ne < CmpOp + def initialize(bits); super(bits, BaseOp::NE); end + end + + class Slt < CmpOp + def initialize(bits); super(bits, BaseOp::SLT); end + end + + class Sle < CmpOp + def initialize(bits); super(bits, BaseOp::SLE); end + end + + class Sgt < CmpOp + def initialize(bits); super(bits, BaseOp::SGT); end + end + + class Sge < CmpOp + def initialize(bits); super(bits, BaseOp::SGE); end + end + + class Ult < CmpOp + def initialize(bits); super(bits, BaseOp::ULT); end + end + + class Ule < CmpOp + def initialize(bits); super(bits, BaseOp::ULE); end + end + + class Ugt < CmpOp + def initialize(bits); super(bits, BaseOp::UGT); end + end + + class Uge < CmpOp + def initialize(bits); super(bits, BaseOp::UGE); end + end + + class ExtendSign < ExtendOp + def initialize(in_bits, out_bits); super(in_bits, out_bits, BaseOp::EXTEND_SIGN); end + end + + class ExtendZero < ExtendOp + def initialize(in_bits, out_bits); super(in_bits, out_bits, BaseOp::EXTEND_ZERO); end + end + + class ExtractLow < ExtractLowOp + def initialize(in_bits, out_bits); super(in_bits, out_bits, BaseOp::EXTRACT_LOW); end + end + + class Popcnt < UnaryOp + def initialize(bits); super(bits, BaseOp::POPCNT); end + end + + class Ctz < UnaryOp + def initialize(bits); super(bits, BaseOp::CTZ); end + end + + class Clz < UnaryOp + def initialize(bits); super(bits, BaseOp::CLZ); end + end + + class Reverse < UnaryOp + def initialize(bits); super(bits, BaseOp::REVERSE); end + end + + class RemU < BinaryOp + def initialize(bits); super(bits, BaseOp::REM_U); end + end + + class RemS < BinaryOp + def initialize(bits); super(bits, BaseOp::REM_S); end + end + + class Ror < BinaryOp + def initialize(bits); super(bits, BaseOp::ROR); end + end + + class Rol < BinaryOp + def initialize(bits); super(bits, BaseOp::ROL); end + end + + class AddOverflow < CmpOp + def initialize(bits); super(bits, BaseOp::ADD_OVERFLOW, out_bits: 1); end + end + + class SubOverflow < CmpOp + def initialize(bits); super(bits, BaseOp::SUB_OVERFLOW, out_bits: 1); end + end + + class DivU < TernaryOp + def initialize(bits); super(bits, BaseOp::DIV_U); end + end + + class DivS < TernaryOp + def initialize(bits); super(bits, BaseOp::DIV_S); end + end + + class Select < Operation + def initialize(bits) + name = "select_#{bits}" + super(name, [], [1, bits, bits], [bits], + semantic_base: BaseOp::SELECT, semantic_func: nil, semantic_table: nil) + check_signature + end + + def check_signature + raise TypeCheckError, 'true/false branches mismatch' unless inputs[1] == inputs[2] && inputs[1] == outputs[0] + end + end +end diff --git a/lib/lira/ir_ser_txt.rb b/lib/lira/ir_ser_txt.rb new file mode 100644 index 0000000..e381376 --- /dev/null +++ b/lib/lira/ir_ser_txt.rb @@ -0,0 +1,39 @@ +# lira/ir_ser_txt.rb +require_relative 'ir' + +def serialize_shape(shape) + "#{shape.lanes_base}#{shape.lanes_mult}" +end + +def deserialize_shape(s) + m = s.match(/^(\d+)(.*)$/) + Shape.new(m[1].to_i, m[2].empty? ? nil : m[2]) +end + +def serialize_statement(stmt) + shape_str = serialize_shape(stmt.shape) + out_parts = stmt.outputs_types.zip(stmt.outputs).flat_map { |typ, name| [typ.to_s, name] } + parts = [shape_str] + out_parts + ['=', stmt.kind, stmt.specifier] + stmt.inputs + parts.join(' ') +end + +def deserialize_statement(s) + m = s.match(/^(\w+)\s+(.*?)\s*=\s*(\w+)\s+(\S+)\s*(.*)$/) + shape_str, outputs_str, kind, specifier, inputs_str = m.captures + shape = deserialize_shape(shape_str) + pairs = outputs_str.split + outputs_types = (0...pairs.length).step(2).map { |i| pairs[i].to_i } + outputs = (1...pairs.length).step(2).map { |i| pairs[i] } + inputs = inputs_str.empty? ? [] : inputs_str.split + Statement.new(shape, outputs, outputs_types, kind, specifier, inputs) +end + +def serialize_statement_seq(seq) + seq.stmts.map { |s| "#{serialize_statement(s)};\n" }.join +end + +def deserialize_statement_seq(s) + raw_stmts = s.split(';').map(&:strip).reject(&:empty?) + stmts = raw_stmts.map { |raw| deserialize_statement(raw) } + StatementSeq.new(stmts) +end diff --git a/lib/lira/ir_std.rb b/lib/lira/ir_std.rb new file mode 100644 index 0000000..84961c1 --- /dev/null +++ b/lib/lira/ir_std.rb @@ -0,0 +1,94 @@ +# lira/ir_std.rb +require_relative 'ir' +require_relative 'arch' + +class StmtInput + attr_accessor :id_ + + def initialize(id_) + @id_ = id_ + end +end + +class StmtOutput + attr_accessor :id_, :value + + def initialize(id_, value) + @id_ = id_ + @value = value + end +end + +class StmtRead + attr_accessor :rf, :rsi + + def initialize(rf, rsi) + @rf = rf + @rsi = rsi + end +end + +class StmtWrite + attr_accessor :rf, :rsi, :value + + def initialize(rf, rsi, value) + @rf = rf + @rsi = rsi + @value = value + end +end + +class StmtOp + attr_accessor :op, :args + + def initialize(op, args) + @op = op + @args = args + end +end + +class StmtEnv + attr_accessor :env, :args + + def initialize(env, args) + @env = env + @args = args + end +end + +class CondEnv + attr_accessor :env, :cond, :on_false, :inputs + + def initialize(env, cond, on_false, inputs) + @env = env + @cond = cond + @on_false = on_false + @inputs = inputs + end +end + +class StmtIndex; end +class StmtConst + attr_accessor :value + def initialize(value); @value = value; end +end +class StmtDynConst + attr_accessor :name + def initialize(name); @name = name; end +end +class StmtGather + attr_accessor :value, :index, :default + def initialize(value, index, default); @value = value; @index = index; @default = default; end +end +class StmtFold + attr_accessor :op, :args + def initialize(op, args); @op = op; @args = args; end +end +class StmtScan + attr_accessor :op, :args + def initialize(op, args); @op = op; @args = args; end +end +class StmtAlias + attr_accessor :semantic, :args + def initialize(semantic, args); @semantic = semantic; @args = args; end +end diff --git a/lib/lira_gen.rb b/lib/lira_gen.rb new file mode 100644 index 0000000..88a2fce --- /dev/null +++ b/lib/lira_gen.rb @@ -0,0 +1,723 @@ +# lira/lira_gen.rb +# !/usr/bin/env ruby + +require_relative 'ADL/base' +require_relative 'ADL/builder' +require_relative 'lira/arch' +require_relative 'lira/ir' +require_relative 'lira/ir_builder' +require_relative 'lira/ir_ops' +require_relative 'lira/arch_ser_yaml' +require_relative 'lira/ir_ser_txt' +require_relative 'Utility/lira_utils' + +class LiraSerializer + OP_MAP = { + add: Lira::Add, + sub: Lira::Sub, + mul: Lira::Mul, + and: Lira::And, + or: Lira::Orr, + xor: Lira::Xor, + shl: Lira::Lsl, + shr: Lira::Lsr, + ashr: Lira::Asr + }.freeze + + CMP_OP_MAP_S = { + eq: Lira::Eq, + ne: Lira::Ne, + lt: Lira::Slt, + gt: Lira::Sgt, + le: Lira::Sle, + ge: Lira::Sge + }.freeze + + CMP_OP_MAP_U = { + eq: Lira::Eq, + ne: Lira::Ne, + lt: Lira::Ult, + gt: Lira::Ugt, + le: Lira::Ule, + ge: Lira::Uge + }.freeze + + class StmtHandler + def initialize(serializer) + @ser = serializer + end + + def handle(stmt) + raise NotImplementedError + end + + private + + attr_reader :ser + + def resolved(op) = ser.resolved(op) + def builder = ser.builder + def vars = ser.vars + def operand_names = ser.operand_names + def arch_builder = ser.arch_builder + def current_instr = ser.current_instr + def store(var, val) = ser.store_result(var, val) + end + + class NewVarHandler < StmtHandler + def handle(stmt) + var = stmt.oprnds[0] + width = ADLToLiraUtils.convert_type(var.type) + width = 1 if width < 1 + vars[var] = builder.seq.new_temp(width) + end + end + + class LetHandler < StmtHandler + def handle(stmt) + rhs_val = resolved(stmt.oprnds[1]) + vars[stmt.oprnds[0]] = rhs_val if rhs_val + end + end + + class DecodeLetHandler < StmtHandler + def handle(stmt) + lhs, rhs = stmt.oprnds + if rhs.is_a?(SimInfra::Var) && rhs.name.to_s.start_with?('f_') + val = ser.resolve_field(rhs) + if val && val.width != ADLToLiraUtils.convert_type(lhs.type) + val = builder.extend_zero(val, ADLToLiraUtils.convert_type(lhs.type)) + end + vars[lhs] = val if val + else + rhs_val = ADLToLiraUtils.resolve_operand(rhs, builder, vars, [], arch_builder) + vars[lhs] = rhs_val if rhs_val + end + end + end + + class AluOpHandler < StmtHandler + def handle(stmt) + name = stmt.name + oprnds = stmt.oprnds + a = resolved(oprnds[1]) + b = resolved(oprnds[2]) + return if a.nil? || b.nil? + + lhs_signed = oprnds[1].type.to_s.start_with?('s') + store(oprnds[0], ser.op_alu(name, lhs_signed, a, b)) + end + end + + class CmpOpHandler < StmtHandler + def handle(stmt) + name = stmt.name + oprnds = stmt.oprnds + a = resolved(oprnds[1]) + b = resolved(oprnds[2]) + return if a.nil? || b.nil? + + unsigned = oprnds[1].type.to_s.start_with?('u') + store(oprnds[0], ser.op_cmp(name, unsigned, a, b)) + end + end + + class SelectHandler < StmtHandler + def handle(stmt) + oprnds = stmt.oprnds + cond = resolved(oprnds[1]) + t = resolved(oprnds[2]) + f = resolved(oprnds[3]) + return if cond.nil? || t.nil? || f.nil? + + store(oprnds[0], builder.select(cond, t, f)) + end + end + + class CastHandler < StmtHandler + def handle(stmt) + name = stmt.name + oprnds = stmt.oprnds + src = resolved(oprnds[1]) + return if src.nil? + + store(oprnds[0], ser.widen_or_truncate(name, oprnds[0].type, src, oprnds[1].type)) + end + end + + class DecodeCastHandler < StmtHandler + def handle(stmt) + name = stmt.name + oprnds = stmt.oprnds + lhs, rhs = oprnds + src = vars[rhs] + if src + vars[lhs] = ser.widen_or_truncate(name, lhs.type, src, rhs.type) + elsif rhs.is_a?(SimInfra::Var) && rhs.name.to_s.start_with?('f_') + val = ser.resolve_field(rhs, lhs.type) + vars[lhs] = val if val + else + rhs_val = ADLToLiraUtils.resolve_operand(rhs, builder, vars, [], arch_builder) + vars[lhs] = rhs_val if rhs_val + end + end + end + + class ExtractHandler < StmtHandler + def handle(stmt) + oprnds = stmt.oprnds + val = resolved(oprnds[1]) + return if val.nil? + + hi = oprnds[2].value + lo = oprnds[3].value + width = hi - lo + 1 + width = 1 if width < 1 + store(oprnds[0], builder.extract_low(val, width)) + end + end + + class ReadRegHandler < StmtHandler + def handle(stmt) + oprnds = stmt.oprnds + reg_var = oprnds[1] + idx = operand_names.index(reg_var.name.to_s) + if idx + reg_num = builder.input(idx, ADLToLiraUtils.convert_type(reg_var.type)) + rf_name = reg_var.regset.to_s + rf = arch_builder.register_files.find { |r| r.name == rf_name } + unless rf + warn "Register file '#{rf_name}' not found, skipping statement" + return + end + store(oprnds[0], builder.read(rf, reg_num)) + else + val = resolved(reg_var) + store(oprnds[0], val) if val + end + end + end + + class WriteRegHandler < StmtHandler + def handle(stmt) + oprnds = stmt.oprnds + reg_var = oprnds[0] + val = resolved(oprnds[1]) + return if val.nil? + + idx = operand_names.index(reg_var.name.to_s) + if idx + reg_num = builder.input(idx, ADLToLiraUtils.convert_type(reg_var.type)) + rf_name = reg_var.regset.to_s + rf = arch_builder.register_files.find { |r| r.name == rf_name } + unless rf + warn "Register file '#{rf_name}' not found, skipping statement" + return + end + builder.write(rf, reg_num, val) + elsif ADLToLiraUtils::SPECIAL_REGS.include?(reg_var.name.to_s) + ef = ADLToLiraUtils.find_env_func(arch_builder, 'setPC', [32], []) + unless ef + warn "Environment function 'setPC' not registered, skipping statement" + return + end + builder.env(ef, [val]) + else + warn "Register file '#{reg_var.regset}' not found, skipping statement" + end + end + end + + class ReadMemHandler < StmtHandler + def handle(stmt) + oprnds = stmt.oprnds + out_type = ADLToLiraUtils.convert_type(oprnds[0].type) + addr = resolved(oprnds[1]) + return if addr.nil? + + ef = ADLToLiraUtils.find_env_func(arch_builder, stmt.attrs.to_s, [addr.width], [out_type]) + unless ef + warn "Environment function #{stmt.attrs} wasn't registered, skipping statement" + return + end + store(oprnds[0], builder.env(ef, [addr])[0]) + end + end + + class WriteMemHandler < StmtHandler + def handle(stmt) + oprnds = stmt.oprnds + addr = resolved(oprnds[0]) + val = resolved(oprnds[1]) + return if addr.nil? || val.nil? + + ef = ADLToLiraUtils.find_env_func(arch_builder, stmt.attrs.to_s, [addr.width, val.width], []) + unless ef + warn "Environment function #{stmt.attrs} wasn't registered, skipping statement" + return + end + builder.env(ef, [addr, val]) + end + end + + class BranchHandler < StmtHandler + def handle(stmt) + target = resolved(stmt.oprnds[0]) + return if target.nil? + + ef = ADLToLiraUtils.find_env_func(arch_builder, 'setPC', [32], []) + unless ef + warn "Environment function 'setPC' not registered, skipping statement" + return + end + builder.env(ef, [target]) + end + end + + class SysCallHandler < StmtHandler + def handle(stmt) + ef = ADLToLiraUtils.find_env_func(arch_builder, 'sysCall', [], []) + unless ef + warn "Environment function 'sysCall' not registered, skipping statement" + return + end + builder.env(ef, []) + end + end + + class RemHandler < StmtHandler + def handle(stmt) + oprnds = stmt.oprnds + a = resolved(oprnds[1]) + b = resolved(oprnds[2]) + return if a.nil? || b.nil? + + unsigned = oprnds[1].type.to_s.start_with?('u') + store(oprnds[0], unsigned ? builder.rem_u(a, b) : builder.rem_s(a, b)) + end + end + + class DivHandler < StmtHandler + def handle(stmt) + oprnds = stmt.oprnds + a = resolved(oprnds[1]) + b = resolved(oprnds[2]) + return if a.nil? || b.nil? + + unsigned = oprnds[1].type.to_s.start_with?('u') + default_val = builder.const(0, a.width) + store(oprnds[0], unsigned ? builder.div_u(a, b, default_val) : builder.div_s(a, b, default_val)) + end + end + + HANDLER_MAP = { + new_var: NewVarHandler, + let: LetHandler, + add: AluOpHandler, + sub: AluOpHandler, + mul: AluOpHandler, + and: AluOpHandler, + or: AluOpHandler, + xor: AluOpHandler, + shl: AluOpHandler, + shr: AluOpHandler, + ashr: AluOpHandler, + + lt: CmpOpHandler, + gt: CmpOpHandler, + le: CmpOpHandler, + ge: CmpOpHandler, + eq: CmpOpHandler, + ne: CmpOpHandler, + + select: SelectHandler, + + cast: CastHandler, zext: CastHandler, + + extract: ExtractHandler, + + readReg: ReadRegHandler, + writeReg: WriteRegHandler, + + readMem: ReadMemHandler, + writeMem: WriteMemHandler, + + branch: BranchHandler, + sysCall: SysCallHandler, + + rem: RemHandler, + div: DivHandler + }.freeze + + DECODE_HANDLER_MAP = HANDLER_MAP.merge( + let: DecodeLetHandler, + cast: DecodeCastHandler, + zext: DecodeCastHandler + ).freeze + + attr_reader :builder, :vars, :operand_names, :arch_builder + attr_accessor :current_instr, :raw_enc + + def initialize(arch_name, arch_attributes = []) + @arch_name = arch_name + @arch_attributes = arch_attributes + @arch_builder = nil + @builder = nil + @vars = nil + @operand_names = nil + @current_instr = nil + + @decode_cache = {} + @decode_snip_id = 0 + @encode_cache = {} + @encode_snip_id = 0 + @constraint_cache = {} + @constraint_snip_id = 0 + @ops_used = {} + end + + def resolved(op) + ADLToLiraUtils.resolve_operand(op, @builder, @vars, @operand_names, @arch_builder) + end + + def store_result(var, val) + @vars[var] = val if val + val + end + + def op_alu(adl_name, lhs_signed, a, b) + op_name = adl_name == :shr && lhs_signed ? :ashr : adl_name + op_class = OP_MAP[op_name] or raise "Unknown ALU op: #{adl_name}" + @builder.op(op_class.new(a.width), [a, b]) + end + + def op_cmp(adl_name, unsigned, a, b) + op_map = unsigned ? CMP_OP_MAP_U : CMP_OP_MAP_S + op_class = op_map[adl_name] or raise "Unknown cmp op: #{adl_name}" + @builder.op(op_class.new(a.width), [a, b]) + end + + def widen_or_truncate(name, dest_type, src, src_type) + dest_width = ADLToLiraUtils.convert_type(dest_type) + dest_width = 1 if dest_width < 1 + src_expected_width = ADLToLiraUtils.convert_type(src_type) + src = @builder.extract_low(src, src_expected_width) if src.width > src_expected_width + return src if src.width == dest_width + + if name == :zext || dest_type.to_s.start_with?('u') + @builder.extend_zero(src, dest_width) + else + @builder.extend_sign(src, dest_width) + end + end + + def resolve_field(field_var, dest_type = :b32) + field_name = field_var.name.to_s[2..] + field = @current_instr.fields.find { |f| f.value.name.to_s == "f_#{field_name}" } + unless field + warn "Field #{field_name} not found" + return nil + end + ADLToLiraUtils.extract_field(@builder, @raw_enc, field, dest_type) + end + + def collect_ops(_seq) + @builder.operations_map.each { |name, op| @ops_used[name] = op } + end + + def stmt_to_lira(stmt) + handler_class = HANDLER_MAP[stmt.name] or raise "Unhandled statement: #{stmt.name}" + handler_class.new(self).handle(stmt) + end + + def generate_semantic(instr) + return Lira::StatementSeq.new([]) if instr.code.tree.empty? + + @builder = Lira::InstructionBuilder.new( + instr.name.to_s, [], [], + Lira::InstructionEncoding.new(32, 0, 0, [], '', '', '') + ) + @vars = {} + @operand_names = collect_operand_names(instr) + instr.code.tree.each { |stmt| stmt_to_lira(stmt) } + seq = @builder.seq.build + collect_ops(seq) + seq + end + + def generate_decode_snippets(instr, operand_vars) + decode_snippet_names = [] + @current_instr = instr + return [] if instr.operand_map.nil? || instr.operand_map.empty? + + instr.operand_list.each do |op_name| + op_scope = instr.operand_map[op_name] + next unless op_scope + + @builder = Lira::SnippetBuilder.new('temp_operand') + @vars = {} + @raw_enc = @builder.input(0, 32) + + op_scope.tree.each do |stmt| + handler_class = DECODE_HANDLER_MAP[stmt.name] + next unless handler_class + + handler_class.new(self).handle(stmt) + end + + val = @vars[op_scope.vars[op_name]] + @builder.output(val || @builder.const(0, 32), 0) + + snippet = @builder.build + collect_ops(snippet.seq) + seq_str = Lira::IrSerTxt.serialize_statement_seq(snippet.seq) + + snip_name = if @decode_cache.key?(seq_str) + @decode_cache[seq_str] + else + unique_name = "decode_#{@decode_snip_id}" + snippet.name = unique_name + @arch_builder.add_snippet(snippet) + @decode_snip_id += 1 + @decode_cache[seq_str] = unique_name + unique_name + end + decode_snippet_names << snip_name + end + + decode_snippet_names + ensure + @current_instr = nil + end + + def generate_encode_snippet(instr, operand_vars) + @builder = Lira::SnippetBuilder.new('temp_encode') + + operand_values = operand_vars.each_with_index.map do |var, idx| + width = ADLToLiraUtils.convert_type(var.type) + width = 1 if width < 1 + @builder.input(idx, width) + end + + base = @builder.const(0, 32) + instr.fields.each do |field| + hi = field.from + lo = field.to + width = hi - lo + 1 + width = 1 if width < 1 + field_var_name = field.value.name.to_s + value_num = field.value.value + if value_num + const_val = @builder.const(value_num, width) + const_val = @builder.extend_zero(const_val, 32) if const_val.width < 32 + shifted = @builder.lsl(const_val, @builder.const(lo, 32)) + base = @builder.orr(base, shifted) + elsif field_var_name =~ /^f_(.+)$/ + rest = $1 + op_high = op_low = nil + operand_name = rest + + # Split immediate fields encode a sub-range of the single `imm` + # operand: `f_imm11_5` -> imm[11:5], `f_imm12` -> imm[12]. + if rest =~ /^imm(\d+)_(\d+)$/ + op_high = ::Regexp.last_match(1).to_i + op_low = ::Regexp.last_match(2).to_i + operand_name = 'imm' + elsif rest =~ /^imm(\d+)$/ + op_high = op_low = ::Regexp.last_match(1).to_i + operand_name = 'imm' + end + + idx = operand_vars.index { |v| v.name.to_s == operand_name } + if idx + op_val = operand_values[idx] + op_val = @builder.extend_zero(op_val, 32) if op_val.width < 32 + + # Extract imm[op_high:op_low] before placing it at the field offset. + if op_low + op_val = @builder.lsr(op_val, @builder.const(op_low, 32)) if op_low != 0 + mask = (1 << (op_high - op_low + 1)) - 1 + op_val = @builder.and_(op_val, @builder.const(mask, 32)) + end + + shifted = @builder.lsl(op_val, @builder.const(lo, 32)) + base = @builder.orr(base, shifted) + end + end + end + + @builder.output(base, 0) + snippet = @builder.build + collect_ops(snippet.seq) + seq_str = Lira::IrSerTxt.serialize_statement_seq(snippet.seq) + + if @encode_cache.key?(seq_str) + @encode_cache[seq_str] + else + unique_name = "encode_#{@encode_snip_id}" + snippet.name = unique_name + @arch_builder.add_snippet(snippet) + @encode_snip_id += 1 + @encode_cache[seq_str] = unique_name + unique_name + end + end + + def generate_constraint_snippet(const_part, const_mask) + @builder = Lira::SnippetBuilder.new('temp_constraint') + raw = @builder.input(0, 32) + masked = @builder.and_(raw, @builder.const(const_mask, 32)) + result = op_cmp(:eq, false, masked, @builder.const(const_part, 32)) + @builder.output(result, 0) + snippet = @builder.build + collect_ops(snippet.seq) + seq_str = Lira::IrSerTxt.serialize_statement_seq(snippet.seq) + + if @constraint_cache.key?(seq_str) + @constraint_cache[seq_str] + else + unique_name = "constraint_#{@constraint_snip_id}" + snippet.name = unique_name + @arch_builder.add_snippet(snippet) + @constraint_snip_id += 1 + @constraint_cache[seq_str] = unique_name + unique_name + end + end + + def collect_operand_vars(instr) + return [] if instr.operand_map.nil? || instr.operand_map.empty? + + instr.operand_list.map { |name| instr.operand_map[name].vars[name] }.compact + end + + def collect_operand_names(instr) + collect_operand_vars(instr).map { |v| v.name.to_s } + end + + def convert_instruction(instr) + operand_vars = collect_operand_vars(instr) + operand_names = collect_operand_names(instr) + @operand_names = operand_names + @current_instr = instr + semantic = generate_semantic(instr) + + decode_snippets = [] + encode_snippet = '' + decode_snippets = generate_decode_snippets(instr, operand_vars) if !instr.operand_map.empty? && operand_vars.any? + encode_snippet = generate_encode_snippet(instr, operand_vars) if instr.fields.any? && operand_vars.any? + + const_part = 0 + const_mask = 0 + instr.fields.each do |field| + value_num = field.value.value + low_bit = field.to + high_bit = field.from + width = high_bit - low_bit + 1 + unless value_num.nil? + const_part |= (value_num << low_bit) + const_mask |= (((1 << width) - 1) << low_bit) + end + end + + operand_sizes = operand_vars.map { |v| ADLToLiraUtils.convert_type(v.type) } + constraint_decode = generate_constraint_snippet(const_part, const_mask) + encoding = Lira::InstructionEncoding.new(32, const_part, const_mask, decode_snippets, encode_snippet, + constraint_decode, '') + Lira::Instruction.new(instr.name.to_s, [], operand_sizes, operand_names, encoding, semantic) + ensure + @current_instr = nil + end + + def build_arch + @arch_builder = Lira::ArchBuilder.new(@arch_name, @arch_attributes) + + SimInfra.class_variable_get(:@@regfiles).each do |rf| + regs = rf.regs.map do |reg| + Lira::Register.new(reg.name.to_s, reg.attrs.map(&:to_s)) + end + lira_rf = Lira::RegisterFile.new(rf.name.to_s, [], Lira::Shape.new(32, nil), regs) + @arch_builder.add_register_file(lira_rf) + end + + env_funcs = SimInfra.interface_functions.dup + env_funcs.uniq! { |f| [f[:name], f[:return_types], f[:argument_types]] } + env_funcs.each do |ef| + inputs = ef[:argument_types].map { |t| ADLToLiraUtils.convert_type(t) } + outputs = ef[:return_types].map { |t| ADLToLiraUtils.convert_type(t) } + lira_ef = Lira::EnvironmentFunction.new(ef[:name].to_s, [], inputs, outputs) + @arch_builder.add_env_func(lira_ef) + end + + @arch_builder.add_env_func(Lira::EnvironmentFunction.new('getPC', [], [], [32])) + @arch_builder.add_env_func(Lira::EnvironmentFunction.new('setPC', [], [32], [])) + + SimInfra.class_variable_get(:@@instructions).each do |instr| + lira_instr = convert_instruction(instr) + @arch_builder.add_instruction(lira_instr) + rescue StandardError => e + warn "Skipping #{instr.name}: #{e}" + end + + @ops_used.each_value do |op| + @arch_builder.add_operation(op) + end + + @arch_builder.build + end +end + +require 'optparse' +require 'pathname' + +def load_target(path) + target_path = Pathname.new(path) + if target_path.directory? + Dir.glob(target_path.join('*.rb')).sort.each do |file| + require_relative file + end + elsif target_path.file? && target_path.extname == '.rb' + require_relative target_path + else + warn "Target '#{path}' is neither a directory nor a .rb file" + exit 1 + end +end + +def extract_arch_name(_loaded_modules) + target_name = nil + instructions = SimInfra.class_variable_get(:@@instructions) + target_name = instructions.first.feature.to_s unless instructions.empty? + target_name ||= 'TargetArch' + [target_name, []] +end + +def main + options = { + output: 'lira.yaml' + } + + OptionParser.new do |opts| + opts.banner = "Usage: #{$0} [options]" + opts.on('-o', '--output FILE', 'Output YAML file (default: lira.yaml)') do |f| + options[:output] = f + end + opts.on('-t', '--target PATH', 'Target architecture directory or .rb file') do |p| + options[:target] = p + end + opts.on('-h', '--help', 'Show this help') do + puts opts + exit + end + end.parse! + + load_target(options[:target]) + + arch_name, arch_attrs = extract_arch_name(nil) + serializer = LiraSerializer.new(arch_name, arch_attrs) + arch = serializer.build_arch + Lira::ArchSerYaml.write_arch(arch, options[:output]) + puts "Serialized architecture to #{options[:output]}" +end + +main if __FILE__ == $0 diff --git a/sim_gen/CMakeLists.txt b/sim_gen/CMakeLists.txt index db5540a..a601fad 100644 --- a/sim_gen/CMakeLists.txt +++ b/sim_gen/CMakeLists.txt @@ -1,4 +1,12 @@ -project(protea_simgen) +# sim_gen/CMakeLists.txt + +project(protea_native_sim_gen) + +if(NOT NATIVE_IR_FILE) + set(NATIVE_IR_FILE ${protea_irgen_BINARY_DIR}/IR.yaml) +endif() + +set(OUTPUT_DIR ${CMAKE_CURRENT_BINARY_DIR}) set(PROTEA_SIMGEN_OUTPUT_HEADERS cpu_state.hh @@ -21,26 +29,33 @@ set(PROTEA_SIMGEN_OUTPUT_FILES ${PROTEA_SIMGEN_OUTPUT_SOURCES} ) -set(IR_YAML_FILE ${protea_irgen_BINARY_DIR}/IR.yaml) - - add_custom_command( OUTPUT ${PROTEA_SIMGEN_OUTPUT_FILES} - COMMAND ${BUNDLE_PATH} exec ruby ${CMAKE_CURRENT_SOURCE_DIR}/sim_gen.rb ${IR_YAML_FILE} - DEPENDS ${IR_YAML_FILE} - COMMENT "Running ${PROJECT_NAME}" + DEPENDS ${NATIVE_IR_FILE} ${CMAKE_CURRENT_SOURCE_DIR}/sim_gen.rb + COMMAND ${CMAKE_COMMAND} -E make_directory ${OUTPUT_DIR} + COMMAND cd ${OUTPUT_DIR} && ${BUNDLE_PATH} exec ruby ${CMAKE_CURRENT_SOURCE_DIR}/sim_gen.rb ${NATIVE_IR_FILE} + COMMENT "Generating native simulator sources from ${NATIVE_IR_FILE}" ) -add_custom_target(${PROJECT_NAME} ALL DEPENDS ${PROTEA_SIMGEN_OUTPUT_FILES}) -add_dependencies(${PROJECT_NAME} protea_irgen) -add_executable(sim ${PROTEA_SIMGEN_OUTPUT_SOURCES} - ${protea_simlib_SOURCE_DIR}/elf_loader.cc - ${protea_simlib_SOURCE_DIR}/base_jit.cc - ${protea_simlib_SOURCE_DIR}/sim.cc +add_custom_target(native_sim_gen DEPENDS ${PROTEA_SIMGEN_OUTPUT_FILES}) +add_dependencies(native_sim_gen native_gen) + +add_executable(sim_native ${PROTEA_SIMGEN_OUTPUT_SOURCES} + ${protea_simlib_SOURCE_DIR}/elf_loader.cc + ${protea_simlib_SOURCE_DIR}/base_jit.cc + ${protea_simlib_SOURCE_DIR}/sim.cc ${protea_simlib_SOURCE_DIR}/jit_factory.cc - ${protea_simlib_SOURCE_DIR}/memory.cc) - -target_include_directories(sim PRIVATE ${CMAKE_CURRENT_BINARY_DIR} ${protea_simlib_SOURCE_DIR}) -target_link_libraries(sim elfio CLI11 fmt) -target_compile_features(sim PRIVATE cxx_std_23) -install(TARGETS sim) + ${protea_simlib_SOURCE_DIR}/memory.cc + ${protea_simlib_SOURCE_DIR}/Target/${TARGET_NAME}/cpu_state_ext.cc) + +target_include_directories(sim_native PRIVATE + ${OUTPUT_DIR} + ${protea_simlib_SOURCE_DIR} +) + +target_link_libraries(sim_native PRIVATE elfio CLI11 fmt) +target_compile_features(sim_native PRIVATE cxx_std_23) +add_dependencies(sim_native native_sim_gen) +add_dependencies(sim_native native_gen) + +install(TARGETS sim_native) diff --git a/sim_gen/CPUState/cpu_state.rb b/sim_gen/CPUState/cpu_state.rb index d75201f..820620f 100644 --- a/sim_gen/CPUState/cpu_state.rb +++ b/sim_gen/CPUState/cpu_state.rb @@ -121,27 +121,37 @@ def generate_read_reg_functions(regfiles) def generate_do_exit_func emitter = Utility::GenEmitter.new emitter.emit_line('// Function to stop the CPU execution') - emitter.emit_line('void doExit() {') + emitter.emit_line('void doExit(isa::Word code) {') emitter.increase_indent emitter.emit_line('m_finished = true;') + emitter.emit_line('m_code = code;') + emitter.emit_line("fmt::println(\"Exiting with code {}...\", code);") emitter.decrease_indent emitter.emit_line('}') emitter.increase_indent_all(2) emitter end - def generate_dump_func(regfiles) + def gen_input_args(args) + args.map { |arg| Utility::HelperCpp.gen_type(arg) }.join(', ') + end + + def generate_interface_func(interface_functions) emitter = Utility::GenEmitter.new - emitter.emit_line("void CPU::dump(std::ostream &ost) const {") - emitter.increase_indent - emitter.emit_line("fmt::println(ost, \"---CPU STATE DUMP---\");") - regfiles.each do |regfile| - regfile[:regs].each do |register| - emitter.emit_line("fmt::print(ost, \"X[{:02}] = {:#010x} \", effIdx, get#{regfile[:name]}());") + emitter.emit_line('// Interface functions') + interface_functions.each do |ifunc| + + if ifunc[:return_types].size == 0 + emitter.emit_line("void #{ifunc[:name]}(#{gen_input_args(ifunc[:argument_types])});") + elsif ifunc[:return_types].size == 1 + emitter.emit_line("#{gen_input_args(ifunc[:return_types])} #{ifunc[:name]}(#{gen_input_args(ifunc[:argument_types])});") + else + emitter.emit_line("std::tuple<#{gen_input_args(ifunc[:return_types])}> #{ifunc[:name]}(#{gen_input_args(ifunc[:argument_types])});") end end - + emitter.emit_blank_line + emitter.increase_indent_all(2) emitter end end @@ -154,7 +164,7 @@ module SimGen module CPUState module Header module_function - + def generate_cpu_state(input_ir) pc_decl = Helper.generate_pc_decl(input_ir[:regfiles]) pc_functions = Helper.generate_pc_functions(input_ir[:regfiles]) @@ -163,6 +173,7 @@ def generate_cpu_state(input_ir) readreg_funcs = Helper.generate_read_reg_functions(input_ir[:regfiles]) do_exit_func = Helper.generate_do_exit_func increase_icount_func = Helper.increase_icount_func + interface_func = Helper.generate_interface_func(input_ir[:interface_functions]) base_type = Utility::HelperCpp.gen_type input_ir[:regfiles][0][:regs][0][:size] "#ifndef GENERATED_#{input_ir[:isa_name].upcase}_CPUSTATE_HH_INCLUDED @@ -173,6 +184,8 @@ def generate_cpu_state(input_ir) #include #include #include +#include +#include namespace prot::memory { class Memory; @@ -194,16 +207,15 @@ class CPU final { // Finished flag bool m_finished{false}; + // Exit code + isa::Word m_code{0}; + explicit CPU(Memory *mem) : m_memory(mem) {} #{setreg_funcs} #{readreg_funcs} #{pc_functions} - #{base_type} getSysCallNum() const; - #{base_type} getSysCallArg(std::size_t num) const; - #{base_type} getSysCallRet() const; - void emulateSysCall(); - +#{interface_func} #{do_exit_func} #{increase_icount_func} diff --git a/sim_gen/Decoders/decoder.rb b/sim_gen/Decoders/decoder.rb index 4d2689f..e8c28f9 100644 --- a/sim_gen/Decoders/decoder.rb +++ b/sim_gen/Decoders/decoder.rb @@ -55,33 +55,35 @@ def get_maj_range(lead_bits) def get_lead_bits(instructions, separ_mask = 0) lead_bits = {} - max_len = instructions.map { |insn| insn[:XLEN] * 8 }.max + max_len = SimGen::Helper::find_max_insn_len(instructions) for bit in 0...max_len - if separ_mask & (1 << bit) != 0 - next - end - + next if (separ_mask & (1 << bit)) != 0 + count_0 = 0 count_1 = 0 - + all_have_bit = true + for insn in instructions insn_mask = calc_insn_mask(insn) insn_value = calc_insn_value(insn) - - if insn_mask & (1 << bit) == 0 - next + + if (insn_mask & (1 << bit)) == 0 + all_have_bit = false + break end - + if (insn_value & (1 << bit)) != 0 count_1 += 1 else count_0 += 1 end end - if count_0 > 0 && count_1 > 0 + + if all_have_bit && count_0 > 0 && count_1 > 0 lead_bits[bit] = [count_0, count_1] end end + lead_bits end @@ -155,12 +157,8 @@ def make_child_tree(node_value, separ_mask, instructions, subtree) def map_operands(insn) operands = {} - cnt = 0 - for node in insn[:map][:tree] - if node[:name] == :new_var && !node[:attrs].nil? && node[:attrs].include?(:op) - operands[node[:oprnds][0][:name]] = "insn.operand#{cnt}" - cnt += 1 - end + (insn[:operand_list] || []).each_with_index do |name, idx| + operands[name] = "insn.operand#{idx}" end operands end @@ -187,8 +185,8 @@ def generate_mapping_body(insn) emitter = Utility::GenEmitter.new operand_map = map_operands(insn) gen = CodeGen::CppGenerator.new(emitter, operand_map) - for node in insn[:map][:tree] - gen.generate_statement(node) + (insn[:operand_map] || {}).each_value do |scope| + (scope[:tree] || []).each { |node| gen.generate_statement(node) } end emitter end diff --git a/sim_gen/ExecEngines/base_exec_engine.rb b/sim_gen/ExecEngines/base_exec_engine.rb index a2247aa..de2c8cd 100644 --- a/sim_gen/ExecEngines/base_exec_engine.rb +++ b/sim_gen/ExecEngines/base_exec_engine.rb @@ -34,7 +34,7 @@ module TranslationUnit module_function def generate_base_exec_engine(input_ir) - max_xlen = SimGen::Helper::find_max_xlen(input_ir[:regfiles]) + max_xlen = SimGen::Helper::find_max_regsize(input_ir[:regfiles]) "#include \"base_exec_engine.hh\" #include \"memory.hh\" diff --git a/sim_gen/ExecEngines/naive_interpreter.rb b/sim_gen/ExecEngines/naive_interpreter.rb index b9fa7d1..7aff60a 100644 --- a/sim_gen/ExecEngines/naive_interpreter.rb +++ b/sim_gen/ExecEngines/naive_interpreter.rb @@ -33,34 +33,14 @@ module TranslationUnit def map_operands(insn) operands = {} - cnt = 0 - insn[:map][:tree].each do |node| - if node[:name] == :new_var && !node[:attrs].nil? && node[:attrs].include?(:op) - operands[node[:oprnds][0][:name]] = "insn.operand#{cnt}" - cnt += 1 - end + (insn[:operand_list] || []).each_with_index do |name, idx| + operands[name] = "insn.operand#{idx}" end operands[:pc] = 'cpu.getPC()' operands end - def cpu_write_reg(dst) - "cpu.set#{dst[:regset]}" - end - - def cpu_read_reg(dst) - "cpu.get#{dst[:regset]}" - end - - def cpu_write_mem(addr, val) - "cpu.m_memory->write(#{addr}, #{val})" - end - - def cpu_read_mem(dst, addr) - "cpu.m_memory->read<#{Utility::HelperCpp.gen_small_type(dst[:type])}>(#{addr})" - end - - def generate_exec_function(instruction) + def generate_exec_function(instruction, funcs) emitter = Utility::GenEmitter.new operand_map = map_operands(instruction) @@ -68,8 +48,13 @@ def generate_exec_function(instruction) emitter.increase_indent gen = CodeGen::CppGenerator.new(emitter, operand_map) + funcs_name = funcs.map { |func| func[:name] } instruction[:code][:tree].each do |node| - gen.generate_statement(node) + if funcs_name.include?(node[:name]) + emitter.emit_line("cpu.#{node[:name]}(#{node[:oprnds].map { |op| op[:name] }.join(', ')});") + else + gen.generate_statement(node) + end end emitter.decrease_indent emitter.emit_line('}') @@ -80,7 +65,7 @@ def generate_exec_function(instruction) def generate_exec_functions(input_ir) emitter = Utility::GenEmitter.new input_ir[:instructions].each do |instruction| - temp_emitter = generate_exec_function(instruction) + temp_emitter = generate_exec_function(instruction, input_ir[:interface_functions]) emitter.concat(temp_emitter) end emitter diff --git a/sim_gen/Hart/hart.rb b/sim_gen/Hart/hart.rb index 8002f12..cb554d1 100644 --- a/sim_gen/Hart/hart.rb +++ b/sim_gen/Hart/hart.rb @@ -52,6 +52,8 @@ class Hart { auto getIcount() const { return m_cpu->m_icount; } + auto getExitCode() const { return m_cpu->m_code; } + public: std::unique_ptr m_mem; std::unique_ptr m_cpu; diff --git a/sim_gen/ISA/isa.rb b/sim_gen/ISA/isa.rb index bed0076..e50683f 100644 --- a/sim_gen/ISA/isa.rb +++ b/sim_gen/ISA/isa.rb @@ -18,14 +18,20 @@ def find_max_operands(instructions) max_operands = 0 max_size = 0 instructions.each do |insn| - operands_count = 0 - insn[:map][:tree].each do |node| - if node[:name] == :new_var && !node[:attrs].nil? && node[:attrs].include?(:op) - operands_count += 1 - max_size = Utility.get_type(node[:oprnds][0][:type]).bitsize if Utility.get_type(node[:oprnds][0][:type]).bitsize > max_size - end - end - max_operands = operands_count if operands_count > max_operands + op_list = insn[:operand_list] || [] + max_operands = op_list.size if op_list.size > max_operands + op_map = insn[:operand_map] || {} + op_list.each do |name| + scope = op_map[name] + next unless scope + new_var_node = (scope[:tree] || []).find { |n| + n[:name] == :new_var && n[:attrs]&.include?(:op) + } + next unless new_var_node + type = new_var_node[:oprnds][0][:type] + bitsize = Utility.get_type(type).bitsize + max_size = bitsize if bitsize > max_size + end end [max_operands, max_size] end @@ -58,7 +64,7 @@ def get_addr_type(instructions) def is_terminator_instruction(insn) insn[:code][:tree].each { |node| - return true if node[:name] == :branch + return true if node[:name] == :branch || node[:name] == :sysCall } false end @@ -98,7 +104,7 @@ def generate_isa_header(input_ir) instruction_struct = Helper.generate_instruction_struct(input_ir) is_terminator_function = Helper.generate_is_terminator_function(input_ir) - max_xlen = SimGen::Helper::find_max_xlen(input_ir[:regfiles]) + max_xlen = SimGen::Helper::find_max_regsize(input_ir[:regfiles]) "#ifndef GENERATED_#{input_ir[:isa_name].upcase}_ISA_HH_INCLUDED #define GENERATED_#{input_ir[:isa_name].upcase}_ISA_HH_INCLUDED @@ -118,7 +124,7 @@ def generate_isa_header(input_ir) inline constexpr std::size_t getILen(Opcode opc) { switch (opc) { - #{input_ir[:instructions].map { |insn| "case Opcode::k#{insn[:name].to_s.upcase}: return #{insn[:XLEN]};" }.join("\n ")} + #{input_ir[:instructions].map { |insn| "case Opcode::k#{insn[:name].to_s.upcase}: return #{insn[:fields].map { |f| f[:from] - f[:to] + 1 }.sum / 8};" }.join("\n ")} default: return 4; } } diff --git a/sim_gen/Utility/sim_utility.rb b/sim_gen/Utility/sim_utility.rb index 0e75596..39e7276 100644 --- a/sim_gen/Utility/sim_utility.rb +++ b/sim_gen/Utility/sim_utility.rb @@ -2,7 +2,7 @@ module SimGen module Helper module_function - def find_max_xlen(regfiles) + def find_max_regsize(regfiles) max_xlen = 0 regfiles.each do |regfile| regfile[:regs].each do |reg| @@ -11,5 +11,14 @@ def find_max_xlen(regfiles) end max_xlen end + + def find_max_insn_len(instructions) + max_len = 0 + instructions.each do |insn| + len_insn = insn[:fields].map { |f| f[:from] - f[:to] + 1 }.sum + max_len = [max_len, len_insn].max + end + max_len + end end end diff --git a/sim_gen/sim_gen.rb b/sim_gen/sim_gen.rb index 572cf54..f3cc91c 100755 --- a/sim_gen/sim_gen.rb +++ b/sim_gen/sim_gen.rb @@ -11,7 +11,7 @@ require 'yaml' -yaml_data = YAML.load_file(ARGV[0]) +yaml_data = YAML.safe_load_file(ARGV[0], permitted_classes: [Symbol], aliases: true) yaml_data[:isa_name] = "RISCV" File.write('cpu_state.hh', SimGen::CPUState::Header.generate_cpu_state(yaml_data)) diff --git a/sim_gen_lira/CMakeLists.txt b/sim_gen_lira/CMakeLists.txt new file mode 100644 index 0000000..f449c5c --- /dev/null +++ b/sim_gen_lira/CMakeLists.txt @@ -0,0 +1,62 @@ +# sim_gen_lira/CMakeLists.txt + +project(protea_lira_sim_gen) + +if(NOT LIRA_IR_FILE) + set(LIRA_IR_FILE ${protea_irgen_BINARY_DIR}/lira.yaml) +endif() + +set(OUTPUT_DIR ${CMAKE_CURRENT_BINARY_DIR}) + +set(PROTEA_SIMGEN_OUTPUT_HEADERS + cpu_state.hh + base_exec_engine.hh + naive_interpreter.hh + decoder.hh + isa.hh + hart.hh +) + +set(PROTEA_SIMGEN_OUTPUT_SOURCES + base_exec_engine.cc + naive_interpreter.cc + decoder.cc + hart.cc +) + +set(PROTEA_SIMGEN_OUTPUT_FILES + ${PROTEA_SIMGEN_OUTPUT_HEADERS} + ${PROTEA_SIMGEN_OUTPUT_SOURCES} +) + + +add_custom_command( + OUTPUT ${PROTEA_SIMGEN_OUTPUT_FILES} + DEPENDS ${LIRA_IR_FILE} ${CMAKE_CURRENT_SOURCE_DIR}/sim_gen.rb + COMMAND ${CMAKE_COMMAND} -E make_directory ${OUTPUT_DIR} + COMMAND cd ${OUTPUT_DIR} && ${BUNDLE_PATH} exec ruby ${CMAKE_CURRENT_SOURCE_DIR}/sim_gen.rb ${LIRA_IR_FILE} + COMMENT "Generating LIRA simulator sources from ${LIRA_IR_FILE}" +) + +add_custom_target(lira_sim_gen DEPENDS ${PROTEA_SIMGEN_OUTPUT_FILES}) +add_dependencies(lira_sim_gen lira_gen) + +add_executable(sim_lira ${PROTEA_SIMGEN_OUTPUT_SOURCES} + ${protea_simlib_SOURCE_DIR}/elf_loader.cc + ${protea_simlib_SOURCE_DIR}/base_jit.cc + ${protea_simlib_SOURCE_DIR}/sim.cc + ${protea_simlib_SOURCE_DIR}/jit_factory.cc + ${protea_simlib_SOURCE_DIR}/memory.cc + ${protea_simlib_SOURCE_DIR}/Target/${TARGET_NAME}/cpu_state_ext.cc +) + +target_include_directories(sim_lira PRIVATE + ${OUTPUT_DIR} + ${protea_simlib_SOURCE_DIR} +) + +target_link_libraries(sim_lira PRIVATE elfio CLI11 fmt) +target_compile_features(sim_lira PRIVATE cxx_std_23) +add_dependencies(sim_lira lira_sim_gen lira_gen) + +install(TARGETS sim_lira) diff --git a/sim_gen_lira/CPUState/cpu_state.rb b/sim_gen_lira/CPUState/cpu_state.rb new file mode 100644 index 0000000..0950373 --- /dev/null +++ b/sim_gen_lira/CPUState/cpu_state.rb @@ -0,0 +1,246 @@ +# frozen_string_literal: true + +require 'lib/Utility/gen_emitter' +require 'Utility/helper_cpp' + +# SimGen - simulation code generator +module SimGen + # SimGen::CPUState - methods for CPUState header generation + module CPUState + # Helper methods for CPUState generation + module Helper + module_function + + def increase_icount_func + emitter = Utility::GenEmitter.new + emitter.emit_line('// Function to increase instruction count') + emitter.emit_line('void increaseICount() {') + emitter.increase_indent + emitter.emit_line('++m_icount;') + emitter.decrease_indent + emitter.emit_line('}') + emitter.increase_indent_all(2) + emitter + end + + def find_pc_reg(regfiles) + regfiles.each do |rf| + rf.regs.each do |reg| + return [reg, rf] if reg.attributes.any? { |a| a.to_s == 'pc' } + end + end + raise 'PC register not found in the register files' + end + + def generate_pc_decl(regfiles) + emitter = Utility::GenEmitter.new + pc_reg, = find_pc_reg(regfiles) + emitter.emit_line('// Program Counter') + emitter.emit_line("#{Utility::HelperCpp.gen_type(regfiles[0].reg_size.lanes_base)} m_pc;") + emitter.emit_blank_line + emitter.increase_indent_all(2) + emitter + end + + def generate_pc_functions(regfiles) + emitter = Utility::GenEmitter.new + pc_reg, = find_pc_reg(regfiles) + pc_type = Utility::HelperCpp.gen_type(regfiles[0].reg_size.lanes_base) + emitter.emit_line('// Set PC function') + emitter.emit_line("void setPC(const #{pc_type} value) {") + emitter.increase_indent + emitter.emit_line('m_pc = value;') + emitter.decrease_indent + emitter.emit_line('}') + emitter.emit_blank_line + emitter.emit_line('// Read PC function') + emitter.emit_line("#{pc_type} getPC() const {") + emitter.increase_indent + emitter.emit_line('return m_pc;') + emitter.decrease_indent + emitter.emit_line('}') + emitter.emit_blank_line + emitter.increase_indent_all(2) + emitter + end + + def generate_cpu_regsets(regfiles) + emitter = Utility::GenEmitter.new + regfiles.each do |rf| + pc_count = rf.regs.count { |r| r.attributes.any? { |a| a.to_s == 'pc' } } + rf_size = rf.regs.size - pc_count + emitter.emit_line("// Register file: #{rf.name}") + regsize = rf.reg_size.lanes_base + array_str = "std::array<#{Utility::HelperCpp.gen_type(regsize)}, #{rf_size}> m_#{rf.name}{};" + emitter.emit_line(array_str) + emitter.emit_blank_line + end + emitter.increase_indent_all(2) + emitter + end + + def generate_if_zero_reg_check(emitter, rf) + rf.regs.each_with_index do |reg, reg_index| + if reg.attributes.any? { |a| a.to_s == 'zero' } + emitter.emit_line("if (reg == #{reg_index}) return; // #{reg.name} is zero register") + end + end + end + + def generate_set_reg_functions(regfiles) + emitter = Utility::GenEmitter.new + regfiles.each do |rf| + emitter.emit_line("// Set register function for #{rf.name}") + emitter.emit_line('template') + emitter.emit_line("void set#{rf.name}(const std::size_t reg, const T value) {") + emitter.increase_indent + generate_if_zero_reg_check(emitter, rf) + emitter.emit_line("m_#{rf.name}[reg] = value;") + emitter.decrease_indent + emitter.emit_line('}') + emitter.emit_blank_line + end + emitter.increase_indent_all(2) + emitter + end + + def generate_read_reg_functions(regfiles) + emitter = Utility::GenEmitter.new + regfiles.each do |rf| + emitter.emit_line("// Read register function for #{rf.name}") + emitter.emit_line('template') + emitter.emit_line("T get#{rf.name}(const std::size_t reg) const {") + emitter.increase_indent + emitter.emit_line("return static_cast(m_#{rf.name}[reg]);") + emitter.decrease_indent + emitter.emit_line('}') + emitter.emit_blank_line + end + emitter.increase_indent_all(2) + emitter + end + + def generate_do_exit_func + emitter = Utility::GenEmitter.new + emitter.emit_line('// Function to stop the CPU execution') + emitter.emit_line('void doExit(isa::Word code) {') + emitter.increase_indent + emitter.emit_line('m_finished = true;') + emitter.emit_line('m_code = code;') + emitter.emit_line("fmt::println(\"Exiting with code {}...\", code);") + emitter.decrease_indent + emitter.emit_line('}') + emitter.increase_indent_all(2) + emitter + end + + def gen_input_args(args) + args.map { |arg| Utility::HelperCpp.gen_type(arg) }.join(', ') + end + + def generate_interface_func(interface_functions) + emitter = Utility::GenEmitter.new + + emitter.emit_line('// Interface functions') + interface_functions.each do |ifunc| + + if ifunc[:return_types].size == 0 + emitter.emit_line("void #{ifunc[:name]}(#{gen_input_args(ifunc[:argument_types])});") + elsif ifunc[:return_types].size == 1 + emitter.emit_line("#{gen_input_args(ifunc[:return_types])} #{ifunc[:name]}(#{gen_input_args(ifunc[:argument_types])});") + else + emitter.emit_line("std::tuple<#{gen_input_args(ifunc[:return_types])}> #{ifunc[:name]}(#{gen_input_args(ifunc[:argument_types])});") + end + end + emitter.emit_blank_line + emitter.increase_indent_all(2) + emitter + end + end + end +end + +# SimGen - simulation code generator +module SimGen + # SimGen::CPUState - methods for CPUState header generation + module CPUState + module Header + module_function + + def generate_cpu_state(arch) + regfiles = arch.register_files + pc_decl = Helper.generate_pc_decl(regfiles) + pc_functions = Helper.generate_pc_functions(regfiles) + regsets_decl = Helper.generate_cpu_regsets(regfiles) + setreg_funcs = Helper.generate_set_reg_functions(regfiles) + readreg_funcs = Helper.generate_read_reg_functions(regfiles) + do_exit_func = Helper.generate_do_exit_func + increase_icount_func = Helper.increase_icount_func + iface = arch.environment_functions.reject { |ef| %w[setPC getPC].include?(ef.name) } + iface_hashes = iface.map { |ef| { name: ef.name, argument_types: ef.inputs, return_types: ef.outputs } } + interface_func = Helper.generate_interface_func(iface_hashes) + + base_type = Utility::HelperCpp.gen_type regfiles[0].reg_size.lanes_base + isa_name = arch.name +"#ifndef GENERATED_#{isa_name.upcase}_CPUSTATE_HH_INCLUDED +#define GENERATED_#{isa_name.upcase}_CPUSTATE_HH_INCLUDED + +#include \"memory.hh\" + +#include +#include +#include +#include +#include + +namespace prot::memory { +class Memory; +} // prot::memory + +namespace prot::state { +using namespace prot::memory; + +class CPU final { +public: +#{regsets_decl} +#{pc_decl} + // Instruction count + std::size_t m_icount{0}; + + // Pointer to memory + Memory *m_memory{nullptr}; + + // Finished flag + bool m_finished{false}; + + // Exit code + isa::Word m_code{0}; + + explicit CPU(Memory *mem) : m_memory(mem) {} + +#{setreg_funcs} +#{readreg_funcs} +#{pc_functions} +#{interface_func} +#{do_exit_func} + +#{increase_icount_func} +}; + +} // prot::state + +#endif // GENERATED_#{isa_name.upcase}_CPUSTATE_HH_INCLUDED +" + end + end + + module TranslationUnit + module_function + + def generate_cpu_state(arch) + # Currently, no implementation is needed for the CPUState translation unit. + '' + end + end + end +end diff --git a/sim_gen_lira/Decoders/decoder.rb b/sim_gen_lira/Decoders/decoder.rb new file mode 100644 index 0000000..b336966 --- /dev/null +++ b/sim_gen_lira/Decoders/decoder.rb @@ -0,0 +1,182 @@ +# Decoders/decoder.rb +require_relative '../cpp_gen' + +module SimGen + module Decoder + module Header + module_function + + def generate_decoder(arch) + <<~CPP + #ifndef GENERATED_#{arch.name.upcase}_DECODER_HH_INCLUDED + #define GENERATED_#{arch.name.upcase}_DECODER_HH_INCLUDED + + #include "isa.hh" + #include + + using namespace prot::isa; + + namespace prot::decoder { + std::optional decode(const uint32_t raw_insn); + } + + #endif + CPP + end + end + + module TranslationUnit + module_function + + def generate_decoder(arch) + snippets = arch.snippets.to_h { |s| [s.name, s.seq] } + tree = build_decode_tree(arch.instructions) + body = emit_tree(tree, 0, snippets, arch.instructions) + <<~CPP + #include "decoder.hh" + #include "snippets.h" + #include + + namespace prot::decoder { + std::optional decode(isa::Word raw_insn) { + Instruction insn{}; + #{body} + return std::nullopt; + } + } + CPP + end + + private + + module_function + + def build_decode_tree(instructions) + # Find the best discriminating bit range + mask_union = instructions.map(&:encoding).map(&:const_mask).reduce(0, :|) + mask_inter = instructions.map(&:encoding).map(&:const_mask).reduce(mask_union, :&) + + # Prefer bits in the intersection (checked by all), then union + work_mask = mask_inter != 0 ? mask_inter : mask_union + + build_tree_node(instructions, work_mask, 0, 31) + end + + def build_tree_node(instructions, work_mask, min_bit, max_bit) + return nil if instructions.empty? + return { leaf: instructions[0] } if instructions.size == 1 + + # Find best discriminating bit range in work_mask + lsb, msb = find_best_range(instructions, work_mask, min_bit, max_bit) + return fallback_linear(instructions) unless lsb + + width = msb - lsb + 1 + mask = ((1 << width) - 1) << lsb + + # Group instructions by their value in this bit range + groups = instructions.group_by { |insn| (insn.encoding.const_encoding_part & mask) >> lsb } + + node = { range: [lsb, msb], children: {} } + groups.each do |val, group| + next_mask = work_mask & ~mask + if group.size == 1 + node[:children][val] = { leaf: group[0] } + else + child = build_tree_node(group, next_mask, min_bit, max_bit) + node[:children][val] = child || { leaf: group[0] } + end + end + node + end + + def find_best_range(instructions, work_mask, min_bit, max_bit) + lead_bits = {} + (min_bit..max_bit).each do |bit| + next if (work_mask & (1 << bit)) == 0 + count_0 = 0 + count_1 = 0 + all_have = true + instructions.each do |insn| + if (insn.encoding.const_mask & (1 << bit)) == 0 + all_have = false + break + end + if (insn.encoding.const_encoding_part & (1 << bit)) != 0 + count_1 += 1 + else + count_0 += 1 + end + end + lead_bits[bit] = [count_0, count_1] if all_have && count_0 > 0 && count_1 > 0 + end + + bits = lead_bits.keys.sort + return nil if bits.empty? + + best_range = [bits[0], bits[0]] + best_score = 0 + (0...bits.size).each do |i| + score = 0 + (i...bits.size).each do |j| + break if bits[j] != bits[i] + (j - i) + score += lead_bits[bits[j]].min + if score > best_score + best_score = score + best_range = [bits[i], bits[j]] + end + end + end + best_range + end + + def fallback_linear(instructions) + { linear: instructions } + end + + def emit_tree(node, indent, snippets, all_instructions) + if node[:leaf] + insn = node[:leaf] + emit_insn_block(insn, indent, snippets) + elsif node[:linear] + node[:linear].map { |insn| emit_insn_block(insn, indent, snippets) }.join("\n") + else + lsb, msb = node[:range] + width = msb - lsb + 1 + mask = ((1 << width) - 1) + shift = lsb + + lines = [] + ind = ' ' * indent + lines << "#{ind}switch ((raw_insn >> #{shift}) & #{mask}) {" + node[:children].sort.each do |val, child| + lines << "#{ind} case #{val}: {" + lines << emit_tree(child, indent + 2, snippets, all_instructions) + lines << "#{ind} }" + end + lines << "#{ind} default: break;" + lines << "#{ind}}" + lines.join("\n") + end + end + + def emit_insn_block(insn, indent, snippets) + constraint_name = insn.encoding.constraint_decode + decode_names = insn.encoding.decode + return '' unless constraint_name && decode_names + + ind = ' ' * indent + operand_calls = decode_names.each_with_index.map { |name, idx| + "insn.operand#{idx} = #{name}(raw_insn);" + }.join("\n#{ind} ") + + <<~CPP + #{ind}if (#{constraint_name}(raw_insn)) { + #{ind} #{operand_calls} + #{ind} insn.m_opc = Opcode::k#{insn.name.to_s.upcase}; + #{ind} return insn; + #{ind}} + CPP + end + end + end +end diff --git a/sim_gen_lira/ExecEngines/base_exec_engine.rb b/sim_gen_lira/ExecEngines/base_exec_engine.rb new file mode 100644 index 0000000..9f42c45 --- /dev/null +++ b/sim_gen_lira/ExecEngines/base_exec_engine.rb @@ -0,0 +1,60 @@ +require "sim_gen/Utility/sim_utility" + +module SimGen + module BaseExecEngine + module Header + module_function + + def generate_base_exec_engine(arch) +<<~CPP +#ifndef GENERATED_#{arch.name.upcase}_EXEC_ENGINE_HH_INCLUDED +#define GENERATED_#{arch.name.upcase}_EXEC_ENGINE_HH_INCLUDED + +#include \"cpu_state.hh\" +#include \"isa.hh\" +#include \"memory.hh\" + +namespace prot::engine { +using namespace prot::state; +using namespace prot::isa; + +struct ExecEngine { + virtual ~ExecEngine() = default; + + virtual void execute(CPU &cpu, const Instruction &insn) = 0; + virtual void step(CPU &cpu); +}; +} // namespace prot::engine + +#endif // GENERATED_#{arch.name.upcase}_EXEC_ENGINE_HH_INCLUDED +CPP + end + end + + module TranslationUnit + module_function + + def generate_base_exec_engine(arch) +<<~CPP +#include "base_exec_engine.hh" +#include "memory.hh" +#include "decoder.hh" + +namespace prot::engine { +using namespace prot::state; +using namespace prot::isa; + +void ExecEngine::step(CPU &cpu) { + const auto bytes = cpu.m_memory->read(cpu.getPC()); + auto instr_opt = decoder::decode(bytes); + if (instr_opt) { + execute(cpu, *instr_opt); + cpu.increaseICount(); + } +} +} // namespace prot::engine +CPP + end + end + end +end diff --git a/sim_gen_lira/ExecEngines/naive_interpreter.rb b/sim_gen_lira/ExecEngines/naive_interpreter.rb new file mode 100644 index 0000000..cde6264 --- /dev/null +++ b/sim_gen_lira/ExecEngines/naive_interpreter.rb @@ -0,0 +1,126 @@ +require_relative '../cpp_gen' + +module SimGen + module NaiveInterpreter + module Header + module_function + + def generate_naive_interpreter(arch) +<<~CPP + #ifndef GENERATED_#{arch.name.upcase}_INTERPRETER_HH_INCLUDED + #define GENERATED_#{arch.name.upcase}_INTERPRETER_HH_INCLUDED + + #include "base_exec_engine.hh" + + namespace prot::engine { + class Interpreter : public ExecEngine { + public: + void execute(CPU &cpu, const Instruction &insn) override; + }; + } + + #endif +CPP + end + end + + module TranslationUnit + module_function + + def generate_naive_interpreter(arch) + exec_functions = [] + branch_insns = [] + + arch.instructions.each do |insn| + name = insn.name.to_s.upcase + seq = insn.semantic + + if seq && seq.stmts.any? + translator = LiraCppGen::Translator.new(seq, :execute, 2) + body = translator.translate + else + body = " // no semantic" + end + + exec_functions << +<<~CPP + void do#{name}(CPU &cpu, const Instruction &insn) { + #{body} + } +CPP + + if seq && seq.stmts.any? && seq.stmts.any? { |s| s.kind == 'env' && s.specifier == 'setPC' } + branch_insns << "case Opcode::k#{name}: return true;" + end + end + + is_branch = +<<~CPP +bool isBranchInstruction(const Instruction &insn) { + switch (insn.m_opc) { + #{branch_insns.join("\n")} + default: return false; + } +} +CPP + + handlers_init = arch.instructions.map do |insn| + "m_handlers[toUnderlying(Opcode::k#{insn.name.to_s.upcase})] = &do#{insn.name.to_s.upcase};" + end.join("\n ") + +<<~CPP +#include "naive_interpreter.hh" +#include "base_ops.h" + +#include +#include + +namespace prot::engine { +using namespace prot::state; +using namespace prot::isa; + +namespace { +#{is_branch} +#{exec_functions.join("\n\n")} + +template +constexpr auto toUnderlying(T val) + requires std::is_enum_v +{ + return static_cast>(val); +} + +class ExecHandlersMap { +public: + using ExecHandler = void (*)(CPU &cpu, const Instruction &insn); +private: + std::array m_handlers{}; +public: + constexpr ExecHandlersMap() { + #{handlers_init} + } + + [[nodiscard]] ExecHandler get(Opcode opcode) const { + assert(toUnderlying(opcode) < m_handlers.size()); + auto toRet = m_handlers[toUnderlying(opcode)]; + assert(toRet != nullptr); + return toRet; + } +}; +constexpr ExecHandlersMap kExecHandlers{}; +} // namespace + +void Interpreter::execute(CPU &cpu, const Instruction &insn) { + const auto handler = kExecHandlers.get(insn.m_opc); + if (!handler) return; + auto oldPC = cpu.getPC(); + handler(cpu, insn); + if (!isBranchInstruction(insn)) + cpu.setPC(oldPC + getILen(insn.m_opc)); +} +} +CPP + end + end + end +end diff --git a/sim_gen_lira/Hart/hart.rb b/sim_gen_lira/Hart/hart.rb new file mode 100644 index 0000000..81e62ef --- /dev/null +++ b/sim_gen_lira/Hart/hart.rb @@ -0,0 +1,112 @@ +module SimGen + module Hart + module Header + module_function + + def get_addr_type(instructions) + instructions.each do |insn| + seq = insn.semantic + next unless seq && seq.stmts + seq.stmts.each do |stmt| + if stmt.kind == 'env' && (stmt.specifier == 'writeMem' || stmt.specifier == 'readMem') + return 32 + end + end + end + 32 + end + + def generate_hart(arch) + type = get_addr_type(arch.instructions) + type_str = Utility::HelperCpp.gen_type(type) + + <<~CPP + #ifndef GENERATED_#{arch.name.upcase}_HART_HH_INCLUDED + #define GENERATED_#{arch.name.upcase}_HART_HH_INCLUDED + + #include + + #include "cpu_state.hh" + #include "elf_loader.hh" + #include "base_exec_engine.hh" + #include "memory.hh" + + namespace prot::hart { + using namespace prot::state; + using namespace prot::isa; + using namespace prot::elf_loader; + using namespace prot::engine; + using namespace prot::memory; + + class Hart { + public: + Hart(std::unique_ptr mem, std::unique_ptr engine); + + void setSP(#{type_str} addr); + + void load(const ElfLoader &loader); + + void setPC(#{type_str} addr); + + void run() { + while (!m_cpu->m_finished) { + m_engine->step(*m_cpu); + } + } + + auto getIcount() const { return m_cpu->m_icount; } + auto getExitCode() const { return m_cpu->m_code; } + + private: + std::unique_ptr m_mem; + std::unique_ptr m_cpu; + std::unique_ptr m_engine; + }; + } + + #endif + CPP + end + end + + module TranslationUnit + module_function + + def get_addr_type(instructions) + instructions.each do |insn| + seq = insn.semantic + next unless seq && seq.stmts + seq.stmts.each do |stmt| + if stmt.kind == 'env' && (stmt.specifier == 'writeMem' || stmt.specifier == 'readMem') + return 32 + end + end + end + 32 + end + + def generate_hart(arch) + type = get_addr_type(arch.instructions) + type_str = Utility::HelperCpp.gen_type(type) + + <<~CPP + #include "hart.hh" + + namespace prot::hart { + Hart::Hart(std::unique_ptr mem, std::unique_ptr engine) + : m_mem(std::move(mem)), m_cpu(std::make_unique(m_mem.get())), + m_engine(std::move(engine)) {} + + void Hart::load(const ElfLoader &loader) { + loader.loadMemory(*m_mem); + setPC(loader.getEntryPoint()); + } + + void Hart::setSP(#{type_str} addr) { m_cpu->setXRegs(2, addr); } + void Hart::setPC(#{type_str} addr) { m_cpu->setPC(addr); } + } + CPP + end + end + end +end diff --git a/sim_gen_lira/ISA/isa.rb b/sim_gen_lira/ISA/isa.rb new file mode 100644 index 0000000..80abc4c --- /dev/null +++ b/sim_gen_lira/ISA/isa.rb @@ -0,0 +1,151 @@ +module SimGen + module ISA + module Helper + module_function + + def find_max_regsize(regfiles) + max = 0 + regfiles.each do |rf| + rf.regs.each do |reg| + max = rf.reg_size.lanes_base if rf.reg_size.lanes_base > max + end + end + max + end + + def find_max_operands(instructions) + max_operands = 0 + max_size = 0 + instructions.each do |insn| + size = insn.operand_sizes.size + max_operands = size if size > max_operands + insn.operand_sizes.each do |s| + max_size = s if s > max_size + end + end + [max_operands, max_size] + end + + def generate_fields_struct(max_operands, max_size) + emitter = Utility::GenEmitter.new + (0...max_operands).each do |i| + emitter.emit_line("uint#{max_size}_t operand#{i};") + end + emitter.increase_indent_all(2) + emitter + end + + def generate_instruction_struct(instructions) + max_operands, max_size = find_max_operands(instructions) + fields_struct = generate_fields_struct(max_operands, max_size) + <<~CPP + struct Instruction { + Opcode m_opc; + #{fields_struct.to_s} + }; + CPP + end + + def is_terminator_instruction(insn) + seq = insn.semantic + return false unless seq && seq.stmts + seq.stmts.any? do |stmt| + stmt.kind == 'env' && (stmt.specifier == 'setPC' || stmt.specifier == 'sysCall') + end + end + + def generate_is_terminator_function(instructions) + emitter = Utility::GenEmitter.new + emitter.emit_line("inline constexpr bool isTerminator(Opcode opc) {") + emitter.increase_indent + emitter.emit_line("switch (opc) {") + emitter.increase_indent + instructions.each do |insn| + if is_terminator_instruction(insn) + emitter.emit_line("case Opcode::k#{insn.name.to_s.upcase}: return true;") + end + end + emitter.emit_line("default: return false;") + emitter.decrease_indent + emitter.emit_line("}") + emitter.decrease_indent + emitter.emit_line("}") + emitter + end + + def get_addr_type_from_instructions(instructions) + instructions.each do |insn| + seq = insn.semantic + next unless seq && seq.stmts + seq.stmts.each do |stmt| + if stmt.kind == 'env' && (stmt.specifier == 'readMem' || stmt.specifier == 'writeMem') + addr_input = stmt.inputs[0] + return 32 + end + end + end + 32 # fallback + end + end + + module Header + module_function + + def generate_isa_header(arch) + isa_name = arch.name + regfiles = arch.register_files + instructions = arch.instructions + + max_xlen = Helper.find_max_regsize(regfiles) + addr_width = Helper.get_addr_type_from_instructions(instructions) + addr_type = case addr_width + when 8 then "uint8_t" + when 16 then "uint16_t" + when 32 then "uint32_t" + when 64 then "uint64_t" + else "uint32_t" + end + + opcode_enum = instructions.map { |insn| " k#{insn.name.to_s.upcase}," }.join("\n") + instruction_struct = Helper.generate_instruction_struct(instructions) + is_terminator_function = Helper.generate_is_terminator_function(instructions) + + get_ilen_lines = instructions.map do |insn| + enc_size = insn.encoding.encoded_size || 32 + len = enc_size / 8 + "case Opcode::k#{insn.name.to_s.upcase}: return #{len};" + end.join("\n ") + + <<~CPP + #ifndef GENERATED_#{isa_name.upcase}_ISA_HH_INCLUDED + #define GENERATED_#{isa_name.upcase}_ISA_HH_INCLUDED + + #include + + namespace prot::isa { + using Addr = #{addr_type}; + using Word = uint#{max_xlen}_t; + + enum class Opcode : uint32_t { + #{opcode_enum} + }; + + #{instruction_struct} + + #{is_terminator_function.to_s} + + inline constexpr std::size_t getILen(Opcode opc) { + switch (opc) { + #{get_ilen_lines} + default: return 4; + } + } + + } // namespace prot::isa + + #endif // GENERATED_#{isa_name.upcase}_ISA_HH_INCLUDED + CPP + end + end + end +end diff --git a/sim_gen_lira/Target/RV32I/cpp_ops.rb b/sim_gen_lira/Target/RV32I/cpp_ops.rb new file mode 100644 index 0000000..ee585b7 --- /dev/null +++ b/sim_gen_lira/Target/RV32I/cpp_ops.rb @@ -0,0 +1,55 @@ +# sim_gen_lira/Target/RISC-V/cpp_ops.rb +# RISC-V ISA-specific C++ codegen overrides. +# Shift operations mask the shift amount to log2(XLEN) bits (RISC-V spec §2.4). + +require_relative '../../cpp_ops' + +module Lira + module CppCodegen + private + + def cpp_type_bits + [1, 8, 16, 32, 64, 128].find { |s| s >= inputs[0] } || 128 + end + end + + class Lsl + def cpp_body + mask = cpp_type_bits - 1 + "b &= #{mask}; return a << b;" + end + end + + class Lsr + def cpp_body + mask = cpp_type_bits - 1 + "b &= #{mask}; return a >> b;" + end + end + + class Asr + def cpp_body + t = Utility::HelperCpp.gen_type(inputs[0]) + ts = Utility::HelperCpp.gen_type(inputs[0], true) + mask = cpp_type_bits - 1 + "b &= #{mask}; return (#{t})((#{ts})a >> b);" + end + end + + class Operation + alias_method :cpp_body_base, :cpp_body + + def cpp_body + case semantic_base + when BaseOp::LSL then "b &= #{cpp_type_bits - 1}; return a << b;" + when BaseOp::LSR then "b &= #{cpp_type_bits - 1}; return a >> b;" + when BaseOp::ASR + t = Utility::HelperCpp.gen_type(inputs[0]) + ts = Utility::HelperCpp.gen_type(inputs[0], true) + "b &= #{cpp_type_bits - 1}; return (#{t})((#{ts})a >> b);" + else + cpp_body_base + end + end + end +end diff --git a/sim_gen_lira/Utility/sim_utility.rb b/sim_gen_lira/Utility/sim_utility.rb new file mode 100644 index 0000000..39e7276 --- /dev/null +++ b/sim_gen_lira/Utility/sim_utility.rb @@ -0,0 +1,24 @@ +module SimGen + module Helper + module_function + + def find_max_regsize(regfiles) + max_xlen = 0 + regfiles.each do |regfile| + regfile[:regs].each do |reg| + max_xlen = [max_xlen, reg[:size]].max + end + end + max_xlen + end + + def find_max_insn_len(instructions) + max_len = 0 + instructions.each do |insn| + len_insn = insn[:fields].map { |f| f[:from] - f[:to] + 1 }.sum + max_len = [max_len, len_insn].max + end + max_len + end + end +end diff --git a/sim_gen_lira/base_ops_gen.rb b/sim_gen_lira/base_ops_gen.rb new file mode 100644 index 0000000..df976ea --- /dev/null +++ b/sim_gen_lira/base_ops_gen.rb @@ -0,0 +1,43 @@ +require 'stringio' +require_relative 'cpp_ops' + +module SimGen + def self.generate_base_ops(operations = []) + out = StringIO.new + generate_file(out, operations) + out.string + end + + class << self + private + + HEADER = <<~'CPP'.freeze +#ifndef LIRA_STD_OPS_H +#define LIRA_STD_OPS_H + +#include + +#ifdef __SIZEOF_INT128__ +typedef unsigned __int128 uint128_t; +typedef __int128 int128_t; +#else +#error "128-bit integers not supported" +#endif +CPP + + FOOTER = "\n#endif\n" + + def generate_file(out, operations) + out.puts HEADER + operations.each { |op| out.puts op.to_cpp } + out.puts + out.puts FOOTER + end + end +end + +if __FILE__ == $0 + output_file = ARGV[0] || 'std_ops.h' + File.write(output_file, SimGen.generate_base_ops) + puts "Generated #{output_file}" +end diff --git a/sim_gen_lira/cpp_gen.rb b/sim_gen_lira/cpp_gen.rb new file mode 100644 index 0000000..60f0f05 --- /dev/null +++ b/sim_gen_lira/cpp_gen.rb @@ -0,0 +1,293 @@ +# frozen_string_literal: true + +require_relative '../lib/lira/ir' +require_relative 'cpp_ops' +require_relative '../lib/Utility/helper_cpp' + +module LiraCppGen + module OpRegistry + @ops = {} + + def self.ops=(map) + @ops = map + end + + def self.lookup(name) + @ops[name] or raise "Unknown operation: #{name}" + end + end + + class StmtEmitter + def initialize(translator) + @t = translator + end + + def stmt + @t.current_stmt + end + + def emit(line) + @t.emit(line) + end + + def indent(&block) + @t.indent(&block) + end + + def context + @t.context + end + + def resolve_var(name) + @t.resolve_var(name, stmt) + end + + def declare(name, width) + @t.declare_var(name, width) + end + + def var_width(name) + @t.var_width(name) + end + + def emit_code + raise NotImplementedError + end + end + + class OpEmitter < StmtEmitter + def emit_code + s = stmt + out = s.outputs[0] + declare(out, s.outputs_types[0]) + inputs = s.inputs.map { |i| resolve_var(i) } + op = OpRegistry.lookup(s.specifier) + + if op.semantic_base == Lira::BaseOp::SELECT + emit("#{out} = #{inputs[0]} ? #{inputs[1]} : #{inputs[2]};") + else + emit("#{out} = #{op.cpp_func_name}(#{inputs.join(', ')});") + end + end + end + + class ConstEmitter < StmtEmitter + def emit_code + s = stmt + out = s.outputs[0] + width = s.outputs_types[0] + return if @t.var_declared?(out) + + emit("#{Utility::HelperCpp.gen_type(width)} #{out} = #{s.specifier};") + @t.register_var(out, width) + end + end + + class DynConstEmitter < StmtEmitter + def emit_code + s = stmt + out = s.outputs[0] + width = s.outputs_types[0] + return if @t.var_declared?(out) + + emit("#{Utility::HelperCpp.gen_type(width)} #{out} = #{s.specifier};") + @t.register_var(out, width) + end + end + + class ReadEmitter < StmtEmitter + def emit_code + s = stmt + rf = s.specifier + idx = resolve_var(s.inputs[0]) + out = s.outputs[0] + width = s.outputs_types[0] + declare(out, width) + if context == :execute + emit("#{out} = cpu.get#{rf}<#{Utility::HelperCpp.gen_type(width)}>(#{idx});") + else + raise "Unexpected read statement in decode section" + end + end + end + + class WriteEmitter < StmtEmitter + def emit_code + s = stmt + rf = s.specifier + idx = resolve_var(s.inputs[0]) + val = resolve_var(s.inputs[1]) + emit("cpu.set#{rf}(#{idx}, #{val});") if context == :execute + end + end + + class EnvEmitter < StmtEmitter + def emit_code + s = stmt + func = s.specifier + inputs = s.inputs.map { |i| resolve_var(i) } + outputs = s.outputs + out_types = s.outputs_types + + func = resolve_env_func(func, out_types, inputs) + + return unless context == :execute + + if outputs.empty? + emit("cpu.#{func}(#{inputs.join(', ')});") + elsif outputs.size == 1 + out = outputs[0] + declare(out, out_types[0]) + emit("#{out} = cpu.#{func}(#{inputs.join(', ')});") + else + outputs.each_with_index { |out, i| declare(out, out_types[i]) } + emit("std::tie(#{outputs.join(', ')}) = cpu.#{func}(#{inputs.join(', ')});") + end + end + + private + + def resolve_env_func(func, out_types, inputs) + if func == 'readMem' && out_types.size == 1 + "readMem#{out_types[0]}" + elsif func == 'writeMem' && inputs.size >= 2 + width = var_width(stmt.inputs[1]) || 32 + "writeMem#{width}" + else + func + end + end + end + + class CondEnvEmitter < StmtEmitter + def emit_code + s = stmt + cond = resolve_var(s.inputs[0]) + inputs = s.inputs[1...-s.outputs.size].map { |i| resolve_var(i) } + on_false = s.inputs[-s.outputs.size..].map { |i| resolve_var(i) } + func = s.specifier + + emit("if (#{cond}) {") + indent { emit_cond_true(s, func, inputs) } + emit('} else {') + indent { emit_cond_false(s, on_false) } + emit('}') + end + + private + + def emit_cond_true(st, func, inputs) + st.outputs.each_with_index do |out, i| + declare(out, st.outputs_types[i]) + emit("#{out} = cpu.#{func}(#{inputs.join(', ')});") + end + end + + def emit_cond_false(st, on_false) + st.outputs.each_with_index { |out, i| emit("#{out} = #{on_false[i]};") } + end + end + + class InputEmitter < StmtEmitter + def emit_code + s = stmt + idx = s.specifier.to_i + out = s.outputs[0] + width = s.outputs_types[0] + declare(out, width) + emit(context == :decode || context == :snippet ? "#{out} = raw_insn;" : "#{out} = insn.operand#{idx};") + end + end + + class OutputEmitter < StmtEmitter + def emit_code + s = stmt + val = resolve_var(s.inputs[0]) + idx = s.specifier.to_i + if context == :snippet + emit("return #{val};") + else + emit("insn.operand#{idx} = #{val};") + end + end + end + + EMITTER_MAP = { + 'op' => OpEmitter, + 'const' => ConstEmitter, + 'dyn_const' => DynConstEmitter, + 'read' => ReadEmitter, + 'write' => WriteEmitter, + 'env' => EnvEmitter, + 'cond_env' => CondEnvEmitter, + 'input' => InputEmitter, + 'output' => OutputEmitter + }.freeze + + class Translator + attr_reader :context, :current_stmt + + def initialize(seq, context = :execute, indent = 0) + @seq = seq + @context = context + @indent = indent + @output = [] + @var_widths = {} + end + + def translate + @seq.stmts.each { |stmt| translate_stmt(stmt) } + @output.join("\n") + end + + def declare_var(name, width) + return if var_declared?(name) + + emit("#{Utility::HelperCpp.gen_type(width)} #{name};") + register_var(name, width) + end + + def var_declared?(name) + @var_widths.key?(name) + end + + def var_width(name) + @var_widths[name] + end + + def register_var(name, width) + @var_widths[name] = width + end + + def resolve_var(name, stmt) + return name if var_declared?(name) + + if name =~ /^_t\d+$/ + width = 32 + width = stmt.outputs_types[0] if stmt.respond_to?(:outputs_types) && stmt.outputs_types.any? + emit("#{Utility::HelperCpp.gen_type(width)} #{name} = 0;") + register_var(name, width) + return name + end + raise "Unknown variable #{name} in #{stmt&.inspect}" + end + + def emit(line) + @output << (' ' * @indent + line) + end + + def indent + @indent += 2 + yield + @indent -= 2 + end + + private + + def translate_stmt(stmt) + emitter_class = EMITTER_MAP[stmt.kind] or raise "Unknown statement kind: #{stmt.kind}" + @current_stmt = stmt + emitter_class.new(self).emit_code + end + end +end diff --git a/sim_gen_lira/cpp_ops.rb b/sim_gen_lira/cpp_ops.rb new file mode 100644 index 0000000..00654af --- /dev/null +++ b/sim_gen_lira/cpp_ops.rb @@ -0,0 +1,310 @@ +require_relative '../lib/lira/ir_ops' +require_relative '../lib/Utility/helper_cpp' + +module Lira + module CppCodegen + def cpp_func_name + name + end + + def cpp_return_type + Utility::HelperCpp.gen_type(outputs[0]) + end + + def cpp_params + inputs.map.with_index { |w, i| + "#{Utility::HelperCpp.gen_type(w)} #{('a'.ord + i).chr}" + }.join(', ') + end + + def cpp_body + case semantic_base + when BaseOp::NOT then 'return ~a;' + when BaseOp::NEG then 'return -a;' + when BaseOp::ADD then 'return a + b;' + when BaseOp::SUB then 'return a - b;' + when BaseOp::MUL then 'return a * b;' + when BaseOp::AND then 'return a & b;' + when BaseOp::ORR then 'return a | b;' + when BaseOp::XOR then 'return a ^ b;' + when BaseOp::EQ then 'return (a == b) ? 1 : 0;' + when BaseOp::NE then 'return (a != b) ? 1 : 0;' + when BaseOp::SLT then signed_cmp('<') + when BaseOp::SLE then signed_cmp('<=') + when BaseOp::SGT then signed_cmp('>') + when BaseOp::SGE then signed_cmp('>=') + when BaseOp::ULT then 'return (a < b) ? 1 : 0;' + when BaseOp::ULE then 'return (a <= b) ? 1 : 0;' + when BaseOp::UGT then 'return (a > b) ? 1 : 0;' + when BaseOp::UGE then 'return (a >= b) ? 1 : 0;' + when BaseOp::LSL then "return a << b;" + when BaseOp::LSR then "return a >> b;" + when BaseOp::ASR then asr_body + when BaseOp::DIV_U then 'return (b == 0) ? c : a / b;' + when BaseOp::DIV_S then div_s_body + when BaseOp::SELECT then 'return a ? b : c;' + when BaseOp::REM_U then rem_u_body + when BaseOp::REM_S then rem_s_body + when BaseOp::EXTEND_SIGN then extend_sign_body + when BaseOp::EXTEND_ZERO then extend_zero_body + when BaseOp::EXTRACT_LOW then extract_low_body + else raise "No cpp_body defined for operation #{semantic_base}" + end + end + + def to_cpp + body_indented = cpp_body.lines.map { |l| " #{l.chomp}" }.join("\n") + <<~CPP + static inline #{cpp_return_type} #{cpp_func_name}(#{cpp_params}) { + #{body_indented} + } + CPP + end + + private + + def signed_cmp(op) + t = Utility::HelperCpp.gen_type(inputs[0], true) + "return ((#{t})a #{op} (#{t})b) ? 1 : 0;" + end + + def asr_body + t = Utility::HelperCpp.gen_type(inputs[0]) + ts = Utility::HelperCpp.gen_type(inputs[0], true) + "return (#{t})((#{ts})a >> b);" + end + + def div_s_body + t = Utility::HelperCpp.gen_type(inputs[0]) + ts = Utility::HelperCpp.gen_type(inputs[0], true) + "return (b == 0) ? c : (#{t})((#{ts})a / (#{ts})b);" + end + + def rem_u_body + <<~CPP + if (b == 0) return a; + return a % b; + CPP + end + + def rem_s_body + t = Utility::HelperCpp.gen_type(inputs[0]) + ts = Utility::HelperCpp.gen_type(inputs[0], true) + <<~CPP + if (b == 0) return a; + #{ts} res = (#{ts})a % (#{ts})b; + return (#{t})res; + CPP + end + + def extend_sign_body + return 'return a;' if inputs[0] == 1 + t = Utility::HelperCpp.gen_type(outputs[0]) + <<~CPP + #{t} val = a & (((#{t})1 << #{inputs[0]}) - 1); + #{t} sign = (val >> (#{inputs[0]} - 1)) & 1; + if (sign) + return val | (~(((#{t})1 << #{inputs[0]}) - 1)); + else + return val; + CPP + end + + def extend_zero_body + t = Utility::HelperCpp.gen_type(outputs[0]) + "return a & (((#{t})1 << #{inputs[0]}) - 1);" + end + + def extract_low_body + t = Utility::HelperCpp.gen_type(inputs[0]) + "return a & (((#{t})1 << #{outputs[0]}) - 1);" + end + end + + class Operation; include CppCodegen; end + class UnaryOp; include CppCodegen; end + class BinaryOp; include CppCodegen; end + class CmpOp; include CppCodegen; end + class TernaryOp; include CppCodegen; end + class ExtendOp; include CppCodegen; end + class ExtractLowOp; include CppCodegen; end + class Select; include CppCodegen; end + + class CmpOp + def cpp_return_type + Utility::HelperCpp.gen_type(1) + end + end + + class ExtendOp + def cpp_return_type + Utility::HelperCpp.gen_type(outputs[0]) + end + + def cpp_params + "#{Utility::HelperCpp.gen_type(inputs[0])} a" + end + end + + class ExtractLowOp + def cpp_return_type + Utility::HelperCpp.gen_type(outputs[0]) + end + + def cpp_params + "#{Utility::HelperCpp.gen_type(inputs[0])} a" + end + end + + class Select + def cpp_params + "uint8_t cond, #{Utility::HelperCpp.gen_type(inputs[1])} a, #{Utility::HelperCpp.gen_type(inputs[1])} b" + end + end + + class ExtendSign + def cpp_body + return 'return a;' if inputs[0] == 1 + t = Utility::HelperCpp.gen_type(outputs[0]) + <<~CPP + #{t} val = a & (((#{t})1 << #{inputs[0]}) - 1); + #{t} sign = (val >> (#{inputs[0]} - 1)) & 1; + if (sign) + return val | (~(((#{t})1 << #{inputs[0]}) - 1)); + else + return val; + CPP + end + end + + class ExtendZero + def cpp_body + t = Utility::HelperCpp.gen_type(outputs[0]) + "return a & (((#{t})1 << #{inputs[0]}) - 1);" + end + end + + class ExtractLow + def cpp_body + t = Utility::HelperCpp.gen_type(inputs[0]) + "return a & (((#{t})1 << #{outputs[0]}) - 1);" + end + end + + class Popcnt + def cpp_body + case inputs[0] + when 8 + <<~CPP + unsigned cnt = 0; + while (a) { cnt += a & 1; a >>= 1; } + return cnt; + CPP + when 16 + 'return popcnt_8(a & 0xFF) + popcnt_8((a >> 8) & 0xFF);' + when 32 + 'return popcnt_16(a & 0xFFFF) + popcnt_16((a >> 16) & 0xFFFF);' + when 64 + 'return popcnt_32(a & 0xFFFFFFFF) + popcnt_32((a >> 32) & 0xFFFFFFFF);' + when 128 + 'return popcnt_64((uint64_t)a) + popcnt_64((uint64_t)(a >> 64));' + else raise 'Unsupported popcnt size' + end + end + end + + class Ctz + def cpp_body + bits = inputs[0] + if bits == 128 + <<~CPP + if (a == 0) return 128; + uint64_t lo = (uint64_t)a; + if (lo) return ctz_64(lo); + else return 64 + ctz_64((uint64_t)(a >> 64)); + CPP + else + <<~CPP + if (a == 0) return #{bits}; + unsigned n = 0; + while ((a & 1) == 0) { a >>= 1; n++; } + return n; + CPP + end + end + end + + class Clz + def cpp_body + bits = inputs[0] + if bits == 128 + <<~CPP + if (a == 0) return 128; + uint64_t hi = (uint64_t)(a >> 64); + if (hi) return clz_64(hi); + else return 64 + clz_64((uint64_t)a); + CPP + else + mask = case bits + when 8 then '0x80' + when 16 then '0x8000' + when 32 then '0x80000000' + when 64 then '0x8000000000000000ULL' + end + <<~CPP + if (a == 0) return #{bits}; + unsigned n = 0; + while ((a & #{mask}) == 0) { a <<= 1; n++; } + return n; + CPP + end + end + end + + class Reverse + def cpp_body + case inputs[0] + when 8 + <<~CPP + a = ((a & 0xF0) >> 4) | ((a & 0x0F) << 4); + a = ((a & 0xCC) >> 2) | ((a & 0x33) << 2); + a = ((a & 0xAA) >> 1) | ((a & 0x55) << 1); + return a; + CPP + when 16 + <<~CPP + a = ((a & 0xFF00) >> 8) | ((a & 0x00FF) << 8); + a = ((a & 0xF0F0) >> 4) | ((a & 0x0F0F) << 4); + a = ((a & 0xCCCC) >> 2) | ((a & 0x3333) << 2); + a = ((a & 0xAAAA) >> 1) | ((a & 0x5555) << 1); + return a; + CPP + when 32 + <<~CPP + a = ((a & 0xFFFF0000) >> 16) | ((a & 0x0000FFFF) << 16); + a = ((a & 0xFF00FF00) >> 8) | ((a & 0x00FF00FF) << 8); + a = ((a & 0xF0F0F0F0) >> 4) | ((a & 0x0F0F0F0F) << 4); + a = ((a & 0xCCCCCCCC) >> 2) | ((a & 0x33333333) << 2); + a = ((a & 0xAAAAAAAA) >> 1) | ((a & 0x55555555) << 1); + return a; + CPP + when 64 + <<~CPP + a = ((a & 0xFFFFFFFF00000000ULL) >> 32) | ((a & 0x00000000FFFFFFFFULL) << 32); + a = ((a & 0xFFFF0000FFFF0000ULL) >> 16) | ((a & 0x0000FFFF0000FFFFULL) << 16); + a = ((a & 0xFF00FF00FF00FF00ULL) >> 8) | ((a & 0x00FF00FF00FF00FFULL) << 8); + a = ((a & 0xF0F0F0F0F0F0F0F0ULL) >> 4) | ((a & 0x0F0F0F0F0F0F0F0FULL) << 4); + a = ((a & 0xCCCCCCCCCCCCCCCCULL) >> 2) | ((a & 0x3333333333333333ULL) << 2); + a = ((a & 0xAAAAAAAAAAAAAAAAULL) >> 1) | ((a & 0x5555555555555555ULL) << 1); + return a; + CPP + when 128 + <<~CPP + uint64_t lo = (uint64_t)a; + uint64_t hi = (uint64_t)(a >> 64); + return ((uint128_t)reverse_64(lo) << 64) | reverse_64(hi); + CPP + end + end + end +end diff --git a/sim_gen_lira/sim_gen.rb b/sim_gen_lira/sim_gen.rb new file mode 100644 index 0000000..c25f578 --- /dev/null +++ b/sim_gen_lira/sim_gen.rb @@ -0,0 +1,35 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +$LOAD_PATH.unshift File.dirname(__FILE__) + +require_relative '../lib/lira/arch_ser_yaml' + +arch = Lira::ArchSerYaml.read_arch(ARGV[0]) +require "Target/#{arch.name}/cpp_ops" + +require_relative 'cpp_gen' +require_relative 'base_ops_gen' +require_relative 'snippets_gen' + +require_relative 'CPUState/cpu_state' +require_relative 'Decoders/decoder' +require_relative 'ISA/isa' +require_relative 'ExecEngines/naive_interpreter' +require_relative 'ExecEngines/base_exec_engine' +require_relative 'Hart/hart' + +LiraCppGen::OpRegistry.ops = arch.operations.to_h { |op| [op.name, op] } + +File.write('cpu_state.hh', SimGen::CPUState::Header.generate_cpu_state(arch)) +File.write('base_exec_engine.hh', SimGen::BaseExecEngine::Header.generate_base_exec_engine(arch)) +File.write('base_exec_engine.cc', SimGen::BaseExecEngine::TranslationUnit.generate_base_exec_engine(arch)) +File.write('naive_interpreter.hh', SimGen::NaiveInterpreter::Header.generate_naive_interpreter(arch)) +File.write('naive_interpreter.cc', SimGen::NaiveInterpreter::TranslationUnit.generate_naive_interpreter(arch)) +File.write('decoder.hh', SimGen::Decoder::Header.generate_decoder(arch)) +File.write('decoder.cc', SimGen::Decoder::TranslationUnit.generate_decoder(arch)) +File.write('isa.hh', SimGen::ISA::Header.generate_isa_header(arch)) +File.write('hart.hh', SimGen::Hart::Header.generate_hart(arch)) +File.write('hart.cc', SimGen::Hart::TranslationUnit.generate_hart(arch)) +File.write('base_ops.h', SimGen.generate_base_ops(arch.operations)) +File.write('snippets.h', SimGen::Snippets.generate_snippets_header(arch.snippets)) diff --git a/sim_gen_lira/snippets_gen.rb b/sim_gen_lira/snippets_gen.rb new file mode 100644 index 0000000..ff9b721 --- /dev/null +++ b/sim_gen_lira/snippets_gen.rb @@ -0,0 +1,35 @@ +require_relative 'cpp_gen' + +module SimGen + module Snippets + module_function + + def generate_snippets_header(snippets) + funcs = snippets + .select { |s| s.name.start_with?('decode_') || s.name.start_with?('constraint_') } + .map { |snip| generate_function(snip.name, snip.seq) } + + <<~CPP + #ifndef GENERATED_SNIPPETS_H + #define GENERATED_SNIPPETS_H + + #include + #include "base_ops.h" + + #{funcs.join("\n\n")} + + #endif + CPP + end + + def generate_function(name, seq) + translator = LiraCppGen::Translator.new(seq, :snippet, 2) + body = translator.translate + <<~CPP + static inline uint32_t #{name}(uint32_t raw_insn) { + #{body} + } + CPP + end + end +end diff --git a/sim_lib/Target/RISCV/cpu_state_ext.cc b/sim_lib/Target/RISCV/cpu_state_ext.cc new file mode 100644 index 0000000..1140fc3 --- /dev/null +++ b/sim_lib/Target/RISCV/cpu_state_ext.cc @@ -0,0 +1,39 @@ +#include "cpu_state.hh" +#include + +namespace prot::state { + +void CPU::sysCall() { + switch (const auto syscallNum = getXRegs(17)) { + case 93: + doExit(getXRegs(10)); + break; + default: + throw std::runtime_error{ + fmt::format("Unknown syscall w/ num {}", syscallNum)}; + } +} + +void CPU::writeMem8(uint32_t addr, uint8_t value) { + m_memory->write(addr, value); +} + +void CPU::writeMem16(uint32_t addr, uint16_t value) { + m_memory->write(addr, value); +} + +void CPU::writeMem32(uint32_t addr, uint32_t value) { + m_memory->write(addr, value); +} + +uint8_t CPU::readMem8(uint32_t addr) { return m_memory->read(addr); } + +uint16_t CPU::readMem16(uint32_t addr) { + return m_memory->read(addr); +} + +uint32_t CPU::readMem32(uint32_t addr) { + return m_memory->read(addr); +} + +} // namespace prot::state diff --git a/sim_lib/base_jit.cc b/sim_lib/base_jit.cc index 247b993..f2f8d2e 100644 --- a/sim_lib/base_jit.cc +++ b/sim_lib/base_jit.cc @@ -8,12 +8,13 @@ extern "C" { } namespace prot::engine { + using namespace prot::isa; using namespace prot::decoder; void JitEngine::step(CPU &cpu) { while (!cpu.m_finished) [[likely]] { - // colllect bb + // collect bb const auto pc = cpu.getPC(); auto found = m_tbCache.lookup(pc); if (found != nullptr) [[likely]] { @@ -30,7 +31,8 @@ void JitEngine::step(CPU &cpu) { auto bytes = cpu.m_memory->read(curAddr); auto inst = decode(bytes); if (!inst.has_value()) { - throw std::runtime_error{"Cannot decode bytes"}; + throw std::runtime_error{"Cannot decode bytes: " + + std::to_string(bytes)}; } bb.insns.push_back(*inst); @@ -51,6 +53,7 @@ void JitEngine::step(CPU &cpu) { interpret(cpu, bbIt->second); } } + void JitEngine::interpret(CPU &cpu, BBInfo &info) { for (const auto &insn : info.insns) { execute(cpu, insn); @@ -94,4 +97,5 @@ CodeHolder::CodeHolder(std::span src) throw std::runtime_error{"Failed to change protection"}; } } + } // namespace prot::engine diff --git a/sim_lib/base_jit.hh b/sim_lib/base_jit.hh index 637a27b..e75e0a1 100644 --- a/sim_lib/base_jit.hh +++ b/sim_lib/base_jit.hh @@ -12,7 +12,9 @@ #include namespace prot::engine { + using JitFunction = void (*)(CPU &); + class JitEngine : public Interpreter { static constexpr std::size_t kExecThreshold = 10; @@ -35,12 +37,14 @@ protected: const auto &entry = get(gpa); return entry.gpa == gpa ? entry.func : nullptr; } + void insert(std::uint32_t gpa, JitFunction func) { get(gpa) = Entry{.func = func, .gpa = gpa}; } private: const Entry &get(std::uint32_t gpa) const { return m_cache[getHash(gpa)]; } + Entry &get(std::uint32_t gpa) { return m_cache[getHash(gpa)]; } [[nodiscard]] static constexpr std::uint32_t getHash(std::uint32_t gpa) { @@ -55,10 +59,12 @@ protected: std::vector insns; std::size_t num_exec{}; }; + [[nodiscard]] const BBInfo *getBBInfo(isa::Addr pc) const; private: void interpret(CPU &cpu, BBInfo &info); + void execute(CPU &cpu, const isa::Instruction &insn) final { Interpreter::execute(cpu, insn); } @@ -94,11 +100,13 @@ public: template [[nodiscard]] auto as() const { return reinterpret_cast(m_data.get()); } + void operator()(CPU &state) const { as()(state); } private: std::unique_ptr m_data; }; + } // namespace prot::engine #endif // INCLUDE_JIT_BASE_HH_INCLUDED diff --git a/sim_lib/elf_loader.cc b/sim_lib/elf_loader.cc index 950cafa..d653ed0 100644 --- a/sim_lib/elf_loader.cc +++ b/sim_lib/elf_loader.cc @@ -1,10 +1,11 @@ #include "elf_loader.hh" -#include - #include +#include + namespace prot::elf_loader { + using namespace prot::memory; ElfLoader::~ElfLoader() = default; @@ -58,4 +59,5 @@ void ElfLoader::loadMemory(Memory &mem) const { std::byte(), seg->get_memory_size() - seg->get_file_size()); } } + } // namespace prot::elf_loader diff --git a/sim_lib/elf_loader.hh b/sim_lib/elf_loader.hh index a75a702..0dee5cc 100644 --- a/sim_lib/elf_loader.hh +++ b/sim_lib/elf_loader.hh @@ -12,11 +12,14 @@ // NOLINTNEXTLINE namespace ELFIO { + // forward decl class elfio; + } // namespace ELFIO namespace prot::elf_loader { + class ElfLoader { public: explicit ElfLoader(std::istream &stream); @@ -35,6 +38,7 @@ private: std::unique_ptr m_elf; }; + } // namespace prot::elf_loader #endif // PROT_ELF_LOADER_HH_INCLUDED diff --git a/sim_lib/jit_factory.cc b/sim_lib/jit_factory.cc index c3c3742..92590f9 100644 --- a/sim_lib/jit_factory.cc +++ b/sim_lib/jit_factory.cc @@ -1,10 +1,12 @@ #include "jit_factory.hh" + #include "base_jit.hh" #include #include namespace prot::engine { + const std::unordered_map()>> JitFactory::kFactories = { @@ -21,8 +23,9 @@ std::vector JitFactory::backends() { std::unique_ptr JitFactory::createEngine(const std::string &backend) { auto it = kFactories.find(backend); - if (it != kFactories.end()) + if (it != kFactories.end()) { return it->second(); + } throw std::invalid_argument("Undefined JIT backend: " + backend); } @@ -30,4 +33,5 @@ JitFactory::createEngine(const std::string &backend) { bool JitFactory::exist(const std::string &backend) { return kFactories.contains(backend); } + } // namespace prot::engine diff --git a/sim_lib/memory.cc b/sim_lib/memory.cc index ec37fdc..35c5438 100644 --- a/sim_lib/memory.cc +++ b/sim_lib/memory.cc @@ -1,9 +1,8 @@ #include "memory.hh" + #include "isa.hh" -#include #include -#include extern "C" { #include @@ -46,9 +45,11 @@ class PlainMemory : public Memory { uint8_t read8(isa::Addr addr) const override { return *reinterpret_cast(translateAddr(addr)); } + uint16_t read16(isa::Addr addr) const override { return *reinterpret_cast(translateAddr(addr)); } + uint32_t read32(isa::Addr addr) const override { return *reinterpret_cast(translateAddr(addr)); } @@ -56,20 +57,22 @@ class PlainMemory : public Memory { void write8(isa::Addr addr, uint8_t val) override { *reinterpret_cast(translateAddr(addr)) = val; } + void write16(isa::Addr addr, uint16_t val) override { *reinterpret_cast(translateAddr(addr)) = val; } + void write32(isa::Addr addr, uint32_t val) override { *reinterpret_cast(translateAddr(addr)) = val; } void writeBlock(std::span src, isa::Addr addr) override { - // checkRange(addr, src.size()); + checkRange(addr, src.size()); std::memcpy(translateAddr(addr), src.data(), src.size()); } void readBlock(isa::Addr addr, std::span dest) const override { - // checkRange(addr, dest.size()); + checkRange(addr, dest.size()); std::memcpy(dest.data(), translateAddr(addr), dest.size()); } diff --git a/sim_lib/memory.hh b/sim_lib/memory.hh index 40b1cfc..a8265d9 100644 --- a/sim_lib/memory.hh +++ b/sim_lib/memory.hh @@ -2,6 +2,7 @@ #define PROT_MEMORY_HH_INCLUDED_MEMORY_HH_INCLUDED #include "isa.hh" + #include #include #include @@ -59,14 +60,19 @@ public: } virtual uint8_t read8(isa::Addr addr) const { return read(addr); } + virtual uint16_t read16(isa::Addr addr) const { return read(addr); } + virtual uint32_t read32(isa::Addr addr) const { return read(addr); } + virtual void write8(isa::Addr addr, uint8_t val) { write(addr, val); } + virtual void write16(isa::Addr addr, uint16_t val) { write(addr, val); } + virtual void write32(isa::Addr addr, uint32_t val) { write(addr, val); } diff --git a/sim_lib/sim.cc b/sim_lib/sim.cc index cc6c9a0..863bd01 100644 --- a/sim_lib/sim.cc +++ b/sim_lib/sim.cc @@ -1,10 +1,3 @@ -#include -#include -#include -#include -#include -#include - #include "base_jit.hh" #include "elf_loader.hh" #include "hart.hh" @@ -12,17 +5,28 @@ #include "memory.hh" #include "naive_interpreter.hh" +#include +#include +#include + +#include +#include +#include + int main(int argc, const char *argv[]) try { std::filesystem::path elfPath; constexpr prot::isa::Addr kDefaultStack = 0x7fffffff; prot::isa::Addr stackTop = kDefaultStack; std::string jitBackend; + bool propagateExit = false; CLI::App app{"Generated simulator with JIT support"}; app.add_option("elf", elfPath, "Path to executable ELF file") ->required() ->check(CLI::ExistingFile); + app.add_flag("--propagate-exit", propagateExit, + "Propagate exit code from guest to host"); app.add_option("--jit", jitBackend, "Use JIT with specified backend") ->check(CLI::IsMember(prot::engine::JitFactory::backends())); @@ -42,7 +46,7 @@ int main(int argc, const char *argv[]) try { prot::hart::Hart hart{prot::memory::makePlain(4ULL << 30U), std::move(engine)}; hart.load(loader); - hart.m_cpu->setXRegs(2, kDefaultStack); + hart.setSP(kDefaultStack); return hart; }(); @@ -56,7 +60,7 @@ int main(int argc, const char *argv[]) try { fmt::println("MIPS: {:.2f}", hart.getIcount() / (duration.count() * 1'000'000)); - return EXIT_SUCCESS; + return propagateExit ? hart.getExitCode() : EXIT_SUCCESS; } catch (const std::exception &ex) { fmt::println(std::cerr, "Caught exception of type {}: {}", typeid(ex).name(), ex.what()); diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt new file mode 100644 index 0000000..9f280e3 --- /dev/null +++ b/test/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(SimTests) diff --git a/test/SimTests/C/CImpl/CMakeLists.txt b/test/SimTests/C/CImpl/CMakeLists.txt new file mode 100644 index 0000000..c8b03b0 --- /dev/null +++ b/test/SimTests/C/CImpl/CMakeLists.txt @@ -0,0 +1,15 @@ +project(tests LANGUAGES C ASM) + +add_subdirectory(Target) + +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) + +macro(protea_add_test tar) + add_executable(${tar} ${ARGN}) + target_link_libraries(${tar} PRIVATE tests_startup) +endmacro() + +add_subdirectory(fib) +add_subdirectory(qsort) +add_subdirectory(xorshift32) +add_subdirectory(zfunc) diff --git a/test/SimTests/C/CImpl/Target/CMakeLists.txt b/test/SimTests/C/CImpl/Target/CMakeLists.txt new file mode 100644 index 0000000..529c48f --- /dev/null +++ b/test/SimTests/C/CImpl/Target/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(${TARGET_NAME}) diff --git a/test/SimTests/C/CImpl/Target/RISCV/CMakeLists.txt b/test/SimTests/C/CImpl/Target/RISCV/CMakeLists.txt new file mode 100644 index 0000000..777f189 --- /dev/null +++ b/test/SimTests/C/CImpl/Target/RISCV/CMakeLists.txt @@ -0,0 +1 @@ +add_library(tests_startup OBJECT start.s) diff --git a/test/SimTests/C/CImpl/Target/RISCV/start.s b/test/SimTests/C/CImpl/Target/RISCV/start.s new file mode 100644 index 0000000..a9b0814 --- /dev/null +++ b/test/SimTests/C/CImpl/Target/RISCV/start.s @@ -0,0 +1,39 @@ +.global _start +.section .text +_start: + li x1, 0 + # li x2, 0 set via sim + li x3, 0 + li x4, 0 + li x5, 0 + li x6, 0 + li x7, 0 + li x8, 0 + li x9, 0 + li x10,0 + li x11,0 + li x12,0 + li x13,0 + li x14,0 + li x15,0 + li x16,0 + li x17,0 + li x18,0 + li x19,0 + li x20,0 + li x21,0 + li x22,0 + li x23,0 + li x24,0 + li x25,0 + li x26,0 + li x27,0 + li x28,0 + li x29,0 + li x30,0 + li x31,0 + + jal main + + li a7, 93 + ecall diff --git a/test/SimTests/C/CImpl/fib/CMakeLists.txt b/test/SimTests/C/CImpl/fib/CMakeLists.txt new file mode 100644 index 0000000..feb9aaf --- /dev/null +++ b/test/SimTests/C/CImpl/fib/CMakeLists.txt @@ -0,0 +1 @@ +protea_add_test(protea_test_fib fib.c) diff --git a/test/SimTests/C/CImpl/fib/fib.c b/test/SimTests/C/CImpl/fib/fib.c new file mode 100644 index 0000000..7e56ac6 --- /dev/null +++ b/test/SimTests/C/CImpl/fib/fib.c @@ -0,0 +1,30 @@ +int verify(unsigned r, unsigned n) { + static const unsigned calculated[] = { + 1, 1, 2, 3, 5, 8, + 13, 21, 34, 55, 89, 144, + 233, 377, 610, 987, 1597, 2584, + 4181, 6765, 10946, 17711, 28657, 46368, + 75025, 121393, 196418, 317811, 514229, 832040, + 1346269, 2178309, 3524578, 5702887, 9227465, 14930352, + 24157817, 39088169, 63245986, 102334155, 165580141, 267914296, + 433494437, 701408733, 1134903170, 1836311903, 2971215073, 512559680, + 3483774753, 3996334433, 3185141890, 2886509027, 1776683621, 368225352, + 2144908973, 2513134325, 363076002, 2876210327, 3239286329, 1820529360, + 764848393, 2585377753, 3350226146, 1640636603, + }; + if (r == calculated[n]) { + return 0; + } + return 1; +} + +unsigned fib(unsigned n) { + if (n <= 1) { + return 1; + } + return fib(n - 1) + fib(n - 2); +} + +int main() { + return verify(fib(16), 16) || verify(fib(20), 20) || verify(fib(31), 31); +} diff --git a/test/SimTests/C/CImpl/qsort/CMakeLists.txt b/test/SimTests/C/CImpl/qsort/CMakeLists.txt new file mode 100644 index 0000000..e3ccb86 --- /dev/null +++ b/test/SimTests/C/CImpl/qsort/CMakeLists.txt @@ -0,0 +1 @@ +protea_add_test(protea_test_qsort qsort.c) diff --git a/test/SimTests/C/CImpl/qsort/dataset1.h b/test/SimTests/C/CImpl/qsort/dataset1.h new file mode 100644 index 0000000..8d28ed9 --- /dev/null +++ b/test/SimTests/C/CImpl/qsort/dataset1.h @@ -0,0 +1,691 @@ + + +#define DATA_SIZE 2048 + +type input_data[DATA_SIZE] = { + 89400484, 976015092, 1792756324, 721524505, 1214379246, 3794415, + 402845420, 2126940990, 1611680320, 786566648, 754215794, 1231249235, + 284658041, 137796456, 2041942843, 329767814, 1255524953, 465119445, + 1731949250, 301663421, 1335861008, 452888789, 14125900, 1231149357, + 2002881120, 730845665, 1913581092, 1275331596, 843738737, 1931282005, + 1492488573, 490920543, 2066865713, 25885333, 238278880, 1898582764, + 250731366, 1612593993, 637659983, 1388759892, 916073297, 1075762632, + 675549432, 937987129, 1417415680, 1508705426, 1663890071, 1746476698, + 686797873, 2109530615, 1459500136, 324215873, 1881253854, 1496277718, + 810387144, 1212974417, 1020037994, 585169793, 2017191527, 556328195, + 1160036198, 1391095995, 1223583276, 1094283114, 436580096, 190215907, + 603159718, 1513255537, 1631935240, 1440145706, 1303736105, 806638567, + 1100041120, 1185825535, 1414141069, 2014090929, 419476096, 1273955724, + 175753599, 1223475486, 574236644, 2046759770, 492507266, 1721767511, + 726141970, 1256152080, 2029909894, 1382429941, 1939683211, 791188057, + 519699747, 1051184301, 1962689485, 706913763, 1776471922, 672906535, + 2005817027, 1274190723, 2119425672, 835063788, 421198539, 1169327477, + 2064145552, 1396662140, 1218522465, 2105638337, 754247044, 2143968639, + 1395289708, 1750443194, 1412540552, 170281493, 389233190, 448284065, + 240618723, 2145930822, 1846605728, 1353999206, 140536987, 1821559709, + 619972089, 1514278798, 750919339, 2143343312, 304427548, 545066288, + 1946004194, 1538069400, 1904770864, 924541465, 567779677, 893302687, + 1239665569, 1157666831, 2105814934, 1505475223, 1636203720, 9736243, + 518073650, 1063743848, 1029176122, 215018112, 1073871430, 1858933377, + 866478506, 1491477359, 477407584, 895562064, 954441852, 638167485, + 1550159640, 614612685, 1453397990, 1334857284, 683536723, 168771888, + 481561285, 755798022, 2016161810, 1162679490, 619428858, 1390306889, + 256860662, 365275089, 1322281086, 1134185180, 1302724177, 621921213, + 837554186, 1711761015, 754896618, 1723143470, 978247260, 1548804416, + 598016845, 1631405417, 790929190, 1602517354, 770957259, 198186681, + 1256015513, 2126029304, 135012885, 583112200, 2118203528, 1834388383, + 866964848, 1695191950, 745183293, 1143511498, 1112731797, 478721193, + 1202162389, 991159735, 1952364329, 519344323, 1667102296, 770412991, + 548632788, 714042223, 1674045273, 1471598258, 1286989824, 1590771096, + 308832070, 959354209, 72802865, 670621648, 269167950, 1598436917, + 2023498746, 1198213061, 2006856683, 1029832956, 1719009954, 1198254803, + 1188748563, 1989240516, 927524181, 1711765426, 1394929399, 769005536, + 2047006719, 1915435344, 618681206, 1431814151, 42021322, 1106678970, + 107160610, 1199317660, 185592115, 1870214195, 205008108, 1834318089, + 948686793, 946311527, 1262399341, 131405125, 1321897861, 1459138745, + 821481684, 852388468, 603907009, 20643769, 1737931879, 37141933, + 2088576982, 366700722, 1761289401, 625991894, 741078359, 817417567, + 969305448, 1152416171, 1101933540, 399456957, 2074896270, 1971484382, + 747592875, 1160333307, 1738353358, 2113434968, 1896952705, 1908093581, + 1155544307, 117766047, 2034767768, 1316120929, 1507433029, 2045407567, + 765386206, 1031625002, 1220915309, 325667019, 1916602098, 16411608, + 47463938, 1379995885, 1221108420, 721046824, 1431492783, 1569479928, + 909415369, 204514903, 933673987, 1565700239, 341674967, 602907378, + 5309142, 849489374, 180599971, 1480437960, 532467027, 1958396887, + 106223060, 1025117441, 935689637, 1752088215, 1704561346, 1568395337, + 1868289345, 569949159, 1045658065, 274746405, 890461390, 507848158, + 793505636, 460893030, 1179525294, 388855203, 1113693824, 13887419, + 1909681194, 1082499152, 1466632447, 1281443423, 612289854, 373305330, + 568652142, 1383640563, 1073695485, 745777837, 624939139, 1289308008, + 1928550562, 148113917, 462743614, 1826880531, 1571598133, 1415390230, + 1480273562, 1331593955, 540006359, 261556590, 1690167792, 283430575, + 1194709162, 1781233744, 649754857, 1434046375, 1135793759, 932423857, + 1170759710, 1048943084, 692845661, 1620562432, 2036750157, 270410557, + 617995659, 1347284277, 1771614266, 30992839, 655445946, 22762734, + 1695617313, 867628573, 1577034674, 227870124, 2063408339, 1512163910, + 787913688, 1758748737, 1553547892, 2072440819, 632611704, 873623623, + 2097057488, 1879635915, 1404727477, 1840896199, 1609955669, 186112992, + 196401930, 130001148, 814302898, 1420810050, 226906236, 1435859758, + 221330186, 329049266, 820933470, 260792255, 1401058771, 210908782, + 1774652096, 886978116, 1807085904, 508041515, 767233910, 26687179, + 318750634, 910677024, 117260224, 2074840378, 301350822, 464795711, + 2053899162, 1335298265, 737518341, 777433215, 1147341731, 1981481446, + 1628389501, 1537459540, 1121432739, 1392162662, 1800522575, 644293952, + 1273223611, 1906345724, 28256901, 1467376771, 372465453, 78348530, + 135678410, 1061864942, 260267972, 1184561748, 287497702, 1154842325, + 1629914848, 2084953915, 799717076, 1382484003, 2045821218, 933603111, + 84924801, 892939912, 279252402, 651750790, 238566180, 942977997, + 1822612008, 1849675857, 939497524, 436630343, 549253917, 1028937430, + 579174666, 2124749673, 880456526, 1451442832, 1350653461, 1546104436, + 858045289, 2129513521, 1181191604, 727587915, 1619598456, 969076419, + 1212628403, 1361078114, 368541415, 333906659, 41714278, 1390274260, + 1563717683, 973769771, 1078197595, 918378387, 1672192305, 1094531762, + 92620223, 2125958841, 1620803320, 915948205, 174965839, 27377406, + 435236973, 1038830638, 1834161399, 305750851, 330474090, 730422541, + 1634445325, 840106059, 767880329, 109526756, 2027814180, 367923081, + 1983379601, 1293091635, 705851791, 226723092, 1067775613, 2082760612, + 951663731, 260670135, 1111213862, 1891630185, 1379259015, 176024101, + 594814862, 1870859970, 1689946986, 1290969161, 244975305, 1296857499, + 1811088032, 1873900475, 1949896838, 1907793490, 592006699, 1312471120, + 509744705, 869853078, 70894786, 503368137, 1686479103, 1602967659, + 1214950832, 1131661227, 768185796, 592234826, 1727583308, 949222447, + 1760851607, 487888229, 1614780688, 1618378831, 602368560, 2028116487, + 183679578, 1561251584, 986240059, 1525451290, 977907387, 432609664, + 1528031307, 116766659, 987761406, 1630293700, 90063199, 114202152, + 543952312, 855107605, 812328969, 88823122, 1092881031, 304131252, + 1505022272, 894769708, 1849495275, 1607515830, 1032748996, 472872107, + 1593359038, 1027760887, 1074205225, 1657001479, 1524491858, 387061281, + 107095939, 1038018856, 798445606, 1486594282, 1878434988, 1558695709, + 2033003588, 373226849, 2133066804, 399991238, 1132597050, 1965358941, + 1551661799, 3522194, 935939763, 2070467093, 500734709, 533101409, + 1068798385, 998931662, 1500102591, 779093898, 66579049, 1121960111, + 749415493, 502323961, 538932155, 259768753, 753296935, 87897457, + 539429964, 1675300017, 1232992084, 420106224, 1685350721, 346598567, + 1610244183, 1597506096, 1079859867, 944382193, 1770497338, 764935753, + 1776794410, 866854601, 365854486, 304211060, 344860208, 1361012693, + 1450892344, 622170346, 70003859, 1681866717, 435288306, 687941098, + 308700094, 1367731096, 1834285819, 255226842, 193873940, 1833603743, + 848402819, 152273285, 231181585, 1754447491, 1838218199, 834410115, + 229905664, 2052321529, 338532526, 77482422, 12937811, 35859252, + 1645969422, 1501181424, 438711458, 1496078411, 419109342, 1455756978, + 1234944834, 1287171290, 470090505, 1900162831, 1130850177, 1772760484, + 381571915, 1605369007, 514914429, 994291574, 1502557594, 1099847920, + 1627355806, 1148699143, 1519017268, 946489895, 106595511, 921573402, + 181567810, 1575380740, 1719573683, 1561730727, 1920182565, 1510133268, + 1102603775, 1175885101, 802730854, 185979744, 1058937717, 1716853034, + 31596852, 462857778, 1335652095, 47036070, 178901145, 1399673078, + 222529745, 128036841, 1708126014, 923768127, 1980923963, 1413860940, + 1382551511, 208160226, 1892370478, 2091626028, 1793190956, 1417601340, + 515811664, 2076612603, 993525189, 1127173529, 245334962, 134453363, + 1206302514, 1344125357, 1139159604, 651536866, 22136821, 1536213818, + 2143324534, 879878312, 1944679691, 119285206, 832081018, 1566878909, + 876130333, 656954306, 226726100, 937976428, 1202009920, 1938258683, + 2014129292, 1274436639, 1102423908, 1485740112, 879552408, 1712269139, + 650513248, 1068587688, 434850545, 382422699, 919736727, 2022291557, + 1319798607, 2139976479, 772059719, 1033910502, 1120963974, 340231765, + 1471131758, 1767380006, 47452797, 1313871880, 399114073, 1462921857, + 671848647, 31574181, 230340298, 239990424, 590690783, 1714295540, + 833019845, 398244682, 522160389, 900852, 1045627895, 1545555937, + 226986415, 208433088, 1502480836, 1611500622, 1933923245, 1588715179, + 1655277291, 1749972876, 1386258142, 935490932, 173822937, 702380578, + 348131466, 81402251, 875481479, 72939206, 2033828953, 1302272656, + 64795664, 2010549018, 1652108025, 58217952, 1871684562, 190536346, + 244709448, 949010757, 320137025, 729474445, 133790520, 740536012, + 316479300, 1191513656, 1802197319, 785398708, 1816641611, 2052328978, + 930367387, 1374125186, 303845878, 852835634, 454359988, 2131761201, + 1757028186, 536063430, 1765354961, 726869128, 1209784819, 1790557628, + 783427298, 2094085507, 1323798820, 846127236, 1065481253, 572240371, + 1745543275, 1011417836, 1970797151, 748527394, 343119399, 723323690, + 925975225, 901789102, 1726987516, 535828217, 387611445, 464171383, + 1170510314, 1166227930, 1807172811, 1942089394, 985305323, 1368235387, + 1691486500, 1568900638, 1876255297, 1249183285, 1710305778, 1763785295, + 1733366374, 1444076976, 1629633514, 2105321510, 225091211, 898893218, + 863551327, 1441811554, 546340809, 1977865396, 2116495484, 1221726287, + 293109484, 1601617797, 1568176414, 1424797596, 1256372950, 298799048, + 1708002892, 829450571, 891710357, 1994402695, 1136264020, 372280769, + 1520667645, 983043723, 1191079043, 680172541, 813511681, 395360213, + 1648575360, 1026342885, 2100497812, 422047044, 509116230, 859612092, + 2037182006, 895080280, 494367164, 1732028080, 355614494, 2141591317, + 1087251698, 580692625, 225934851, 1581062145, 1515262458, 1497680539, + 1711718534, 1774796872, 301673313, 1136356724, 653050943, 109035776, + 1709823304, 1340949553, 1365423458, 1155459206, 1203897636, 188016786, + 256210446, 633075975, 19227407, 1864952910, 1143853106, 237020443, + 1750197960, 856837002, 80321564, 1679324299, 1257507406, 1390040163, + 1590461855, 806384435, 1331383316, 2027828650, 1649392096, 1928309762, + 1027758817, 1267173039, 123889599, 95752736, 2060969286, 619461174, + 1686215900, 1817156134, 2118821565, 1596821127, 1800186189, 212821393, + 661318748, 1123331233, 146002907, 953877041, 1771924274, 929351822, + 2142357746, 356638683, 1610539590, 2001056977, 368889391, 62209567, + 1775608361, 992410365, 1336108161, 696448050, 333820982, 585804640, + 1775805177, 809604334, 93191015, 732444124, 1492071476, 1930662128, + 174082258, 340442582, 507936866, 362748128, 1607204293, 953383750, + 1599876594, 416457166, 571635069, 1356847855, 267174620, 2011827638, + 1572212863, 589049769, 2024853642, 1680251429, 914906004, 398911194, + 795915364, 1332467446, 688483428, 628445699, 578787063, 2006320950, + 1167207852, 336213879, 1640952769, 1778544166, 1617229086, 190807078, + 1968608155, 2122852959, 31153367, 1353144470, 2196420, 1395155215, + 1948121717, 69118708, 2140091269, 2530146, 1740778973, 1601247294, + 1205895814, 858150908, 1878253960, 1967705762, 2090543533, 1702425249, + 622114437, 1192155877, 1095403694, 2115445751, 1201124879, 1140728569, + 2085323316, 1291025252, 871908043, 863647665, 1245819051, 1468486929, + 631022494, 1161580432, 539942311, 1943137808, 1826628136, 259775677, + 277497333, 2140756121, 973493986, 1121800211, 1539560507, 1337406065, + 186178768, 482917205, 1459100749, 1924603748, 390743779, 1140008063, + 517767440, 1764436465, 722260205, 1400929335, 1706528514, 486165509, + 1379460673, 206653795, 3159407, 565150174, 688338919, 1223572435, + 2122262571, 513009937, 1390656632, 271906847, 1622692876, 1313115559, + 2061144988, 411864717, 437710825, 513582947, 305489695, 1713188647, + 387273799, 1901537567, 644842409, 1231932661, 356672421, 232170581, + 1636860706, 302219842, 2094591332, 1697686200, 1390477985, 1833543700, + 1203377492, 50968578, 1332379148, 1514582723, 909273561, 1914809801, + 560663378, 1032914339, 1216475831, 113462155, 1165446977, 800591831, + 1058677375, 432102601, 2131797509, 1175004233, 1602827413, 878884686, + 446372159, 257728183, 800661980, 1387864976, 2004770236, 999229412, + 1428223489, 175843632, 74887898, 630393584, 1147793249, 112648605, + 1028529524, 1891904961, 1953919896, 481563348, 436476038, 1601134240, + 72319656, 1581118537, 460420451, 1904576737, 786297537, 359735266, + 1918354829, 4031164, 1679777458, 1144017176, 1462192184, 690865719, + 1515933932, 363508800, 1480324438, 1044088643, 2036061488, 218671081, + 830595166, 381933797, 108346070, 92271196, 217762975, 1522316172, + 1021014457, 1407094080, 857894203, 1968623233, 1459620801, 1345014111, + 709651138, 520511102, 2048560397, 1768795266, 1013901419, 1709697877, + 1026380990, 1377995642, 1560142576, 542609105, 1534330971, 528024121, + 2015847175, 325324443, 1137511396, 1883999260, 1871060346, 715940689, + 167653495, 1292049996, 1172290275, 2018336444, 1951228823, 1666074170, + 1834852613, 854475547, 308857120, 502558280, 2105718728, 1624653209, + 514214340, 976063110, 227427283, 912381406, 785989696, 451448729, + 212046016, 2068743361, 117280545, 1936668087, 210748671, 1984152603, + 945948973, 1409001936, 1644353864, 1139018167, 678475375, 1279061703, + 723930558, 195379046, 1498554338, 999346398, 1665914525, 1473735214, + 1561422777, 151416112, 697817760, 1622758049, 607761482, 69889880, + 1152335090, 1063657548, 1338090388, 55461678, 1278053582, 837024327, + 1914764659, 1049475248, 161502390, 80404202, 624714335, 879380479, + 1066787659, 1375470750, 1561212123, 59384706, 966363087, 2044016080, + 1178086274, 1159745061, 291298358, 173062659, 1385675177, 652078020, + 1802327778, 1555660285, 623909040, 1579725218, 1649344003, 270814499, + 350182379, 1188076819, 893957771, 534384094, 1057003814, 230634042, + 2117880007, 778834747, 250859482, 104637677, 1328272543, 1869264274, + 1847908587, 311127477, 506466155, 1808237662, 607471900, 1558244592, + 1228817775, 720339756, 1963053072, 1011473945, 1204992245, 566166447, + 419053054, 737377568, 520329478, 1740099311, 1682700783, 1455316979, + 2118805956, 729509794, 1565610678, 722347551, 739596391, 882282387, + 926200942, 999899279, 1318032594, 122124863, 1633512617, 1269707634, + 380070610, 1043920511, 665601851, 873976891, 717911282, 2135673182, + 761851297, 1604330946, 666624765, 513561613, 1504023310, 1128895624, + 99511825, 722919148, 1047336724, 550532376, 1082864732, 289686472, + 216557804, 1174587016, 845698678, 1554106660, 577410402, 790256415, + 675663963, 2029133999, 161450336, 228960529, 743745539, 1352833750, + 2123379476, 852338021, 1291070368, 448708980, 1953450944, 923478775, + 827496819, 1126017956, 197964832, 281317274, 1171925835, 764902582, + 595717488, 2129930580, 1437147036, 1447469119, 755554593, 2130879949, + 1835203128, 1547662666, 1855359256, 965490116, 672323245, 182598318, + 216435361, 1324723894, 1144669754, 454438520, 1220523503, 1520886946, + 1797641070, 1585050246, 797060176, 1821482472, 2128078174, 973367349, + 991874801, 679519053, 1961647235, 2094159153, 391321675, 1604357658, + 576906032, 1712341869, 344515114, 1122768484, 1659079595, 1328885292, + 48775768, 247448424, 1836119534, 1564061243, 1386366954, 485818381, + 37017340, 356546370, 1675494182, 430093707, 1959222232, 1784682542, + 1839063567, 1596042792, 295666215, 403378386, 2114587535, 1515528736, + 1541546082, 1444048519, 1215103809, 1687941280, 1546057655, 1905279500, + 544899032, 2069178089, 1688652157, 1414160501, 332201519, 631936923, + 423299667, 1332937015, 545602285, 310273032, 960982228, 372501343, + 1933532372, 1711569347, 11476473, 155845605, 700725671, 1457464894, + 1325083914, 172109594, 664387510, 1705378439, 376781122, 1472567100, + 343682568, 1370528050, 265363198, 2079492652, 1803183394, 519194709, + 1538391713, 1931493432, 1183464058, 1489699243, 495097609, 801046035, + 177100916, 1292413659, 1348373925, 1550525411, 697685269, 856621012, + 1992941115, 1189141368, 221661515, 156760399, 38620214, 375863194, + 2078528215, 2103236982, 341987235, 698660475, 381094614, 1201152163, + 1275500498, 398211404, 801610475, 1087556673, 846650758, 1848681194, + 1287830283, 1400070607, 1603428054, 1233022905, 810516965, 690710531, + 1860435620, 750631050, 1271370220, 860360715, 1189323192, 1913926325, + 946425090, 1815408878, 743572345, 1902501708, 1276205250, 2005653265, + 624614472, 2108439398, 1952177514, 964348374, 1171051384, 2126963607, + 812288356, 108628319, 980702956, 714456194, 1678967663, 1935271536, + 236851791, 1541132933, 1066014062, 1607628402, 1926717418, 954942098, + 1733982669, 14239125, 1506716966, 848141854, 1178260876, 614222093, + 731606176, 1512135729, 63244522, 968848252, 1783943137, 1402735006, + 1355391150, 1659137391, 1173889730, 1042942541, 1318900244, 1149113346, + 2090025563, 1201659316, 250022739, 1035075488, 674580901, 1090386021, + 1943651015, 934048997, 2087660971, 738682048, 1305071296, 91177380, + 1708106609, 1685880008, 364589031, 1860839427, 1927367009, 906899219, + 1090443335, 892574149, 1969729134, 1874026715, 927045887, 1159898528, + 730296520, 349249331, 317980803, 225908941, 483348027, 1035956563, + 241537930, 1279981214, 1247518755, 247447060, 1793747608, 752388169, + 288054543, 2073482870, 2039012903, 617768643, 433412593, 499898207, + 1050512245, 331284679, 851322111, 1294873695, 1715379173, 1159675637, + 1029338154, 2027445678, 1653332243, 1874855959, 1234157881, 260674360, + 1042790263, 1401980800, 730090881, 1745393357, 1550721460, 1607677838, + 969500483, 778702716, 1765830270, 731763278, 1600023202, 1957728250, + 690983, 444361278, 1278777407, 1231639101, 597427397, 1087245613, + 258177907, 2093472294, 1462778368, 2067100479, 1628387880, 762564955, + 1194041213, 1348361229, 1822279764, 1826590258, 1112056034, 2088786920, + 815110420, 1957877704, 1087195269, 881982271, 1945110368, 1656527154, + 529233847, 137046551, 522408049, 1880577483, 847255974, 851716534, + 925604268, 1037521069, 461527795, 1332620900, 525605961, 1389787451, + 1127911377, 1198857033, 859385989, 706825946, 371790550, 145611377, + 655200896, 1900613055, 1333790305, 1101722351, 1278794420, 2089981667, + 1150780072, 13180701, 1502266386, 1103013140, 343038558, 1897907456, + 1612609979, 1209991461, 1740783613, 1643991754, 977454680, 787842886, + 163362230, 1087742330, 200253206, 1691676526, 360632817, 1787338655, + 35595330, 822635252, 1834254978, 1372169786, 1063768444, 973490494, + 697866347, 156498369, 169293723, 180549009, 112035400, 127867199, + 241711645, 2004664325, 23288667, 1997381015, 736455241, 1986921372, + 1570645300, 2067499753, 1463269859, 148527979, 618168829, 1715279374, + 2066440075, 2118433006, 198233440, 1835860030, 1345873587, 1902595458, + 1961619988, 1291438802, 1325008187, 836983022, 1849657867, 500376868, + 1599565995, 1705905941, 1600493361, 386733714, 1028820236, 1663100626, + 1322696419, 1482983072, 1092382563, 1667679197, 1965855212, 1063839036, + 1742032331, 300191208, 620497725, 503895325, 2094864173, 928179911, + 277942057, 1677449797, 1249086623, 799527371, 1180063064, 48311975, + 1866094167, 1405763119, 2109851473, 1594621666, 580464203, 1752598186, + 1339293088, 922186026, 1403771494, 299505702, 1345987999, 1298200648, + 2128826472, 677220745, 831273447, 741184696, 696188251, 1912065710, + 1016469330, 682018288, 353946286, 559509624, 515414188, 1852181952, + 407771887, 812094461, 1859683061, 1100089300, 498702377, 653626077, + 765701205, 150878039, 328551896, 77104822, 1775331228, 1835977906, + 706357381, 1240287664, 839507573, 1054066034, 1823053058, 701959731, + 82879528, 652404808, 866097476, 926939064, 1326017288, 1747861289, + 1173840088, 1524006589, 443704960, 835506582, 5363460, 2068343250, + 1683915700, 2080735477, 1913489530, 951256529, 1752318678, 105384223, + 1788389051, 1787391786, 1430821640, 540952308, 882484999, 690806365, + 202502890, 1593837351, 530093821, 385878401, 907401151, 378912543, + 454746323, 251514112, 1451277631, 1125822965, 21289266, 1642884452, + 804368379, 2048205721, 917508270, 1514792012, 139494505, 1143168018, + 115016418, 1730333306, 1630776459, 50748643, 1745247524, 1313640711, + 1076198976, 1820281480, 941471466, 806673335, 722162727, 1837280287, + 705508794, 2088955494, 510497580, 51692325, 893597382, 1373978529, + 1007042224, 685006165, 1471461419, 1555325521, 1215063385, 1424859828, + 657251271, 1391827090, 965562483, 604275115, 1285258674, 341475746, + 294191106, 633240394, 1897691227, 1904243956, 823532901, 1577955754, + 2016464961, 1862876260, 577384103, 1012611702, 247243083, 636485510, + 1952805989, 1447876480, 108021700, 1016615447, 2047769687, 943871886, + 787537653, 12744598, 853545598, 334037304, 553373537, 1089408490, + 497867498, 2038925801, 1434633879, 1290629443, 75922980, 957037315, + 2130252471, 477317888, 952824381, 1686570783, 459340678, 751885764, + 836307572, 2027909489, 28791588, 322748588, 1335236478, 787106123, + 113580144, 954915740, 1317077622, 1299667896, 2009244921, 1548588723, + 2049698913, 732388681, 1781891230, 2090684129, 993786972, 1959292396, + 1336513734, 691093904, 1746904676, 935573751, 1123555638, 108413311, + 1445352642, 169726789, 123352211, 1635952299, 673775121, 2042861943, + 757787251, 512494446, 119656942, 58159196, 2090570016, 486181025, + 1619641914, 432990571, 894937325, 379470588, 1890938638, 1886317932, + 1858637614, 969358207, 1230449468, 1890889527, 351741654, 214725897, + 1550012286, 308005013, 26292400, 68067591, 1383307838, 1746273091, + 1090104632, 1658037573, 2081544705, 1133473813, 1680294422, 1050373352, + 1806061681, 1713475126, 520699193, 417568373, 1355086853, 631399565, + 1742434188, 2077667592, 1709019727, 594054971, 937081176, 742185643, + 1904514273, 887841601, 1288684086, 424587711, 1497926365, 829844031, + 1384314543, 250129297, 200083737, 693737559, 1527022962, 1462501905, + 1687540458, 1156824624, 241481265, 1190890142, 1250360726, 2064308502, + 27563032, 1880483834, 1984143440, 104727360, 1324123626, 1089710430, + 1403206383, 1930880552, 773197243, 1160186023, 562994480, 1065136414, + 502237764, 1642338733, 1310177444, 1730721241, 1475638246, 615734453, + 1160537912, 928836931, 253898558, 1799210492, 1205522527, 413058646, + 1589194592, 1774218355, 43955934, 1673314595, 683393460, 1260859787, + 2098829619, 772503535, 1232567659, 758174758, 831270563, 1605294199, + 1660678300, 24379565, 1426483935, 1611558740, 1085326591, 12849216, + 455856722, 878692218, 1910978116, 1382893830, 1950124297, 950009818, + 904287249, 791384486, 1584408128, 210098472, 1110387095, 364620240, + 53868166, 772251062, 472745168, 1133910514, 1715402379, 1445225855, + 1541125975, 149171217, 972058766, 1893095488, 1487620835, 640835502, + 1470285405, 646688705, 988431201, 703130341, 1753125385, 1985895474, + 696002734, 1783233173, 1317201705, 1755204784, 532132334, 1069450170, + 249700039, 524320231, 757959820, 2109052886, 604977130, 1971654864, + 1588222158, 1533496974, 623670976, 1405668251, 1955436051, 1082881617, + 1387039848, 874153641, 1345378476, 1168465459, 2005021017, 234039217, + 473318229, 654912216, 1473166451, 997649666, 801824335, 2052343947, + 1883168929, 185658088, 1389954587, 1725541486, 885873448, 958774566, + 2054212564, 60536525, 1427504270, 1160285859, 1827651881, 1408805003, + 1684018729, 61716770, 844057079, 1011596733, 1521350211, 1581801257, + 907554175, 2022973269, 1125104871, 1312064004, 1466679625, 970194422, + 80900939, 1445279202, 335456148, 510478312, 92860378, 1646704157, + 1650899832, 1533447203, 268087516, 880688023, 1180525723, 1868151949, + 1750955971, 401446720, 540093580, 1022861633, 461442838, 1222554291, + 456462271, 94760711, 1231111410, 2145073408, 1932108837, 300618464, + 2055783490, 980863365, 1308872551, 1010427073, 1399854717, 1217804021, + 934700736, 878744414}; + +type verify_data[DATA_SIZE] = { + 690983, 900852, 2196420, 2530146, 3159407, 3522194, + 3794415, 4031164, 5309142, 5363460, 9736243, 11476473, + 12744598, 12849216, 12937811, 13180701, 13887419, 14125900, + 14239125, 16411608, 19227407, 20643769, 21289266, 22136821, + 22762734, 23288667, 24379565, 25885333, 26292400, 26687179, + 27377406, 27563032, 28256901, 28791588, 30992839, 31153367, + 31574181, 31596852, 35595330, 35859252, 37017340, 37141933, + 38620214, 41714278, 42021322, 43955934, 47036070, 47452797, + 47463938, 48311975, 48775768, 50748643, 50968578, 51692325, + 53868166, 55461678, 58159196, 58217952, 59384706, 60536525, + 61716770, 62209567, 63244522, 64795664, 66579049, 68067591, + 69118708, 69889880, 70003859, 70894786, 72319656, 72802865, + 72939206, 74887898, 75922980, 77104822, 77482422, 78348530, + 80321564, 80404202, 80900939, 81402251, 82879528, 84924801, + 87897457, 88823122, 89400484, 90063199, 91177380, 92271196, + 92620223, 92860378, 93191015, 94760711, 95752736, 99511825, + 104637677, 104727360, 105384223, 106223060, 106595511, 107095939, + 107160610, 108021700, 108346070, 108413311, 108628319, 109035776, + 109526756, 112035400, 112648605, 113462155, 113580144, 114202152, + 115016418, 116766659, 117260224, 117280545, 117766047, 119285206, + 119656942, 122124863, 123352211, 123889599, 127867199, 128036841, + 130001148, 131405125, 133790520, 134453363, 135012885, 135678410, + 137046551, 137796456, 139494505, 140536987, 145611377, 146002907, + 148113917, 148527979, 149171217, 150878039, 151416112, 152273285, + 155845605, 156498369, 156760399, 161450336, 161502390, 163362230, + 167653495, 168771888, 169293723, 169726789, 170281493, 172109594, + 173062659, 173822937, 174082258, 174965839, 175753599, 175843632, + 176024101, 177100916, 178901145, 180549009, 180599971, 181567810, + 182598318, 183679578, 185592115, 185658088, 185979744, 186112992, + 186178768, 188016786, 190215907, 190536346, 190807078, 193873940, + 195379046, 196401930, 197964832, 198186681, 198233440, 200083737, + 200253206, 202502890, 204514903, 205008108, 206653795, 208160226, + 208433088, 210098472, 210748671, 210908782, 212046016, 212821393, + 214725897, 215018112, 216435361, 216557804, 217762975, 218671081, + 221330186, 221661515, 222529745, 225091211, 225908941, 225934851, + 226723092, 226726100, 226906236, 226986415, 227427283, 227870124, + 228960529, 229905664, 230340298, 230634042, 231181585, 232170581, + 234039217, 236851791, 237020443, 238278880, 238566180, 239990424, + 240618723, 241481265, 241537930, 241711645, 244709448, 244975305, + 245334962, 247243083, 247447060, 247448424, 249700039, 250022739, + 250129297, 250731366, 250859482, 251514112, 253898558, 255226842, + 256210446, 256860662, 257728183, 258177907, 259768753, 259775677, + 260267972, 260670135, 260674360, 260792255, 261556590, 265363198, + 267174620, 268087516, 269167950, 270410557, 270814499, 271906847, + 274746405, 277497333, 277942057, 279252402, 281317274, 283430575, + 284658041, 287497702, 288054543, 289686472, 291298358, 293109484, + 294191106, 295666215, 298799048, 299505702, 300191208, 300618464, + 301350822, 301663421, 301673313, 302219842, 303845878, 304131252, + 304211060, 304427548, 305489695, 305750851, 308005013, 308700094, + 308832070, 308857120, 310273032, 311127477, 316479300, 317980803, + 318750634, 320137025, 322748588, 324215873, 325324443, 325667019, + 328551896, 329049266, 329767814, 330474090, 331284679, 332201519, + 333820982, 333906659, 334037304, 335456148, 336213879, 338532526, + 340231765, 340442582, 341475746, 341674967, 341987235, 343038558, + 343119399, 343682568, 344515114, 344860208, 346598567, 348131466, + 349249331, 350182379, 351741654, 353946286, 355614494, 356546370, + 356638683, 356672421, 359735266, 360632817, 362748128, 363508800, + 364589031, 364620240, 365275089, 365854486, 366700722, 367923081, + 368541415, 368889391, 371790550, 372280769, 372465453, 372501343, + 373226849, 373305330, 375863194, 376781122, 378912543, 379470588, + 380070610, 381094614, 381571915, 381933797, 382422699, 385878401, + 386733714, 387061281, 387273799, 387611445, 388855203, 389233190, + 390743779, 391321675, 395360213, 398211404, 398244682, 398911194, + 399114073, 399456957, 399991238, 401446720, 402845420, 403378386, + 407771887, 411864717, 413058646, 416457166, 417568373, 419053054, + 419109342, 419476096, 420106224, 421198539, 422047044, 423299667, + 424587711, 430093707, 432102601, 432609664, 432990571, 433412593, + 434850545, 435236973, 435288306, 436476038, 436580096, 436630343, + 437710825, 438711458, 443704960, 444361278, 446372159, 448284065, + 448708980, 451448729, 452888789, 454359988, 454438520, 454746323, + 455856722, 456462271, 459340678, 460420451, 460893030, 461442838, + 461527795, 462743614, 462857778, 464171383, 464795711, 465119445, + 470090505, 472745168, 472872107, 473318229, 477317888, 477407584, + 478721193, 481561285, 481563348, 482917205, 483348027, 485818381, + 486165509, 486181025, 487888229, 490920543, 492507266, 494367164, + 495097609, 497867498, 498702377, 499898207, 500376868, 500734709, + 502237764, 502323961, 502558280, 503368137, 503895325, 506466155, + 507848158, 507936866, 508041515, 509116230, 509744705, 510478312, + 510497580, 512494446, 513009937, 513561613, 513582947, 514214340, + 514914429, 515414188, 515811664, 517767440, 518073650, 519194709, + 519344323, 519699747, 520329478, 520511102, 520699193, 522160389, + 522408049, 524320231, 525605961, 528024121, 529233847, 530093821, + 532132334, 532467027, 533101409, 534384094, 535828217, 536063430, + 538932155, 539429964, 539942311, 540006359, 540093580, 540952308, + 542609105, 543952312, 544899032, 545066288, 545602285, 546340809, + 548632788, 549253917, 550532376, 553373537, 556328195, 559509624, + 560663378, 562994480, 565150174, 566166447, 567779677, 568652142, + 569949159, 571635069, 572240371, 574236644, 576906032, 577384103, + 577410402, 578787063, 579174666, 580464203, 580692625, 583112200, + 585169793, 585804640, 589049769, 590690783, 592006699, 592234826, + 594054971, 594814862, 595717488, 597427397, 598016845, 602368560, + 602907378, 603159718, 603907009, 604275115, 604977130, 607471900, + 607761482, 612289854, 614222093, 614612685, 615734453, 617768643, + 617995659, 618168829, 618681206, 619428858, 619461174, 619972089, + 620497725, 621921213, 622114437, 622170346, 623670976, 623909040, + 624614472, 624714335, 624939139, 625991894, 628445699, 630393584, + 631022494, 631399565, 631936923, 632611704, 633075975, 633240394, + 636485510, 637659983, 638167485, 640835502, 644293952, 644842409, + 646688705, 649754857, 650513248, 651536866, 651750790, 652078020, + 652404808, 653050943, 653626077, 654912216, 655200896, 655445946, + 656954306, 657251271, 661318748, 664387510, 665601851, 666624765, + 670621648, 671848647, 672323245, 672906535, 673775121, 674580901, + 675549432, 675663963, 677220745, 678475375, 679519053, 680172541, + 682018288, 683393460, 683536723, 685006165, 686797873, 687941098, + 688338919, 688483428, 690710531, 690806365, 690865719, 691093904, + 692845661, 693737559, 696002734, 696188251, 696448050, 697685269, + 697817760, 697866347, 698660475, 700725671, 701959731, 702380578, + 703130341, 705508794, 705851791, 706357381, 706825946, 706913763, + 709651138, 714042223, 714456194, 715940689, 717911282, 720339756, + 721046824, 721524505, 722162727, 722260205, 722347551, 722919148, + 723323690, 723930558, 726141970, 726869128, 727587915, 729474445, + 729509794, 730090881, 730296520, 730422541, 730845665, 731606176, + 731763278, 732388681, 732444124, 736455241, 737377568, 737518341, + 738682048, 739596391, 740536012, 741078359, 741184696, 742185643, + 743572345, 743745539, 745183293, 745777837, 747592875, 748527394, + 749415493, 750631050, 750919339, 751885764, 752388169, 753296935, + 754215794, 754247044, 754896618, 755554593, 755798022, 757787251, + 757959820, 758174758, 761851297, 762564955, 764902582, 764935753, + 765386206, 765701205, 767233910, 767880329, 768185796, 769005536, + 770412991, 770957259, 772059719, 772251062, 772503535, 773197243, + 777433215, 778702716, 778834747, 779093898, 783427298, 785398708, + 785989696, 786297537, 786566648, 787106123, 787537653, 787842886, + 787913688, 790256415, 790929190, 791188057, 791384486, 793505636, + 795915364, 797060176, 798445606, 799527371, 799717076, 800591831, + 800661980, 801046035, 801610475, 801824335, 802730854, 804368379, + 806384435, 806638567, 806673335, 809604334, 810387144, 810516965, + 812094461, 812288356, 812328969, 813511681, 814302898, 815110420, + 817417567, 820933470, 821481684, 822635252, 823532901, 827496819, + 829450571, 829844031, 830595166, 831270563, 831273447, 832081018, + 833019845, 834410115, 835063788, 835506582, 836307572, 836983022, + 837024327, 837554186, 839507573, 840106059, 843738737, 844057079, + 845698678, 846127236, 846650758, 847255974, 848141854, 848402819, + 849489374, 851322111, 851716534, 852338021, 852388468, 852835634, + 853545598, 854475547, 855107605, 856621012, 856837002, 857894203, + 858045289, 858150908, 859385989, 859612092, 860360715, 863551327, + 863647665, 866097476, 866478506, 866854601, 866964848, 867628573, + 869853078, 871908043, 873623623, 873976891, 874153641, 875481479, + 876130333, 878692218, 878744414, 878884686, 879380479, 879552408, + 879878312, 880456526, 880688023, 881982271, 882282387, 882484999, + 885873448, 886978116, 887841601, 890461390, 891710357, 892574149, + 892939912, 893302687, 893597382, 893957771, 894769708, 894937325, + 895080280, 895562064, 898893218, 901789102, 904287249, 906899219, + 907401151, 907554175, 909273561, 909415369, 910677024, 912381406, + 914906004, 915948205, 916073297, 917508270, 918378387, 919736727, + 921573402, 922186026, 923478775, 923768127, 924541465, 925604268, + 925975225, 926200942, 926939064, 927045887, 927524181, 928179911, + 928836931, 929351822, 930367387, 932423857, 933603111, 933673987, + 934048997, 934700736, 935490932, 935573751, 935689637, 935939763, + 937081176, 937976428, 937987129, 939497524, 941471466, 942977997, + 943871886, 944382193, 945948973, 946311527, 946425090, 946489895, + 948686793, 949010757, 949222447, 950009818, 951256529, 951663731, + 952824381, 953383750, 953877041, 954441852, 954915740, 954942098, + 957037315, 958774566, 959354209, 960982228, 964348374, 965490116, + 965562483, 966363087, 968848252, 969076419, 969305448, 969358207, + 969500483, 970194422, 972058766, 973367349, 973490494, 973493986, + 973769771, 976015092, 976063110, 977454680, 977907387, 978247260, + 980702956, 980863365, 983043723, 985305323, 986240059, 987761406, + 988431201, 991159735, 991874801, 992410365, 993525189, 993786972, + 994291574, 997649666, 998931662, 999229412, 999346398, 999899279, + 1007042224, 1010427073, 1011417836, 1011473945, 1011596733, 1012611702, + 1013901419, 1016469330, 1016615447, 1020037994, 1021014457, 1022861633, + 1025117441, 1026342885, 1026380990, 1027758817, 1027760887, 1028529524, + 1028820236, 1028937430, 1029176122, 1029338154, 1029832956, 1031625002, + 1032748996, 1032914339, 1033910502, 1035075488, 1035956563, 1037521069, + 1038018856, 1038830638, 1042790263, 1042942541, 1043920511, 1044088643, + 1045627895, 1045658065, 1047336724, 1048943084, 1049475248, 1050373352, + 1050512245, 1051184301, 1054066034, 1057003814, 1058677375, 1058937717, + 1061864942, 1063657548, 1063743848, 1063768444, 1063839036, 1065136414, + 1065481253, 1066014062, 1066787659, 1067775613, 1068587688, 1068798385, + 1069450170, 1073695485, 1073871430, 1074205225, 1075762632, 1076198976, + 1078197595, 1079859867, 1082499152, 1082864732, 1082881617, 1085326591, + 1087195269, 1087245613, 1087251698, 1087556673, 1087742330, 1089408490, + 1089710430, 1090104632, 1090386021, 1090443335, 1092382563, 1092881031, + 1094283114, 1094531762, 1095403694, 1099847920, 1100041120, 1100089300, + 1101722351, 1101933540, 1102423908, 1102603775, 1103013140, 1106678970, + 1110387095, 1111213862, 1112056034, 1112731797, 1113693824, 1120963974, + 1121432739, 1121800211, 1121960111, 1122768484, 1123331233, 1123555638, + 1125104871, 1125822965, 1126017956, 1127173529, 1127911377, 1128895624, + 1130850177, 1131661227, 1132597050, 1133473813, 1133910514, 1134185180, + 1135793759, 1136264020, 1136356724, 1137511396, 1139018167, 1139159604, + 1140008063, 1140728569, 1143168018, 1143511498, 1143853106, 1144017176, + 1144669754, 1147341731, 1147793249, 1148699143, 1149113346, 1150780072, + 1152335090, 1152416171, 1154842325, 1155459206, 1155544307, 1156824624, + 1157666831, 1159675637, 1159745061, 1159898528, 1160036198, 1160186023, + 1160285859, 1160333307, 1160537912, 1161580432, 1162679490, 1165446977, + 1166227930, 1167207852, 1168465459, 1169327477, 1170510314, 1170759710, + 1171051384, 1171925835, 1172290275, 1173840088, 1173889730, 1174587016, + 1175004233, 1175885101, 1178086274, 1178260876, 1179525294, 1180063064, + 1180525723, 1181191604, 1183464058, 1184561748, 1185825535, 1188076819, + 1188748563, 1189141368, 1189323192, 1190890142, 1191079043, 1191513656, + 1192155877, 1194041213, 1194709162, 1198213061, 1198254803, 1198857033, + 1199317660, 1201124879, 1201152163, 1201659316, 1202009920, 1202162389, + 1203377492, 1203897636, 1204992245, 1205522527, 1205895814, 1206302514, + 1209784819, 1209991461, 1212628403, 1212974417, 1214379246, 1214950832, + 1215063385, 1215103809, 1216475831, 1217804021, 1218522465, 1220523503, + 1220915309, 1221108420, 1221726287, 1222554291, 1223475486, 1223572435, + 1223583276, 1228817775, 1230449468, 1231111410, 1231149357, 1231249235, + 1231639101, 1231932661, 1232567659, 1232992084, 1233022905, 1234157881, + 1234944834, 1239665569, 1240287664, 1245819051, 1247518755, 1249086623, + 1249183285, 1250360726, 1255524953, 1256015513, 1256152080, 1256372950, + 1257507406, 1260859787, 1262399341, 1267173039, 1269707634, 1271370220, + 1273223611, 1273955724, 1274190723, 1274436639, 1275331596, 1275500498, + 1276205250, 1278053582, 1278777407, 1278794420, 1279061703, 1279981214, + 1281443423, 1285258674, 1286989824, 1287171290, 1287830283, 1288684086, + 1289308008, 1290629443, 1290969161, 1291025252, 1291070368, 1291438802, + 1292049996, 1292413659, 1293091635, 1294873695, 1296857499, 1298200648, + 1299667896, 1302272656, 1302724177, 1303736105, 1305071296, 1308872551, + 1310177444, 1312064004, 1312471120, 1313115559, 1313640711, 1313871880, + 1316120929, 1317077622, 1317201705, 1318032594, 1318900244, 1319798607, + 1321897861, 1322281086, 1322696419, 1323798820, 1324123626, 1324723894, + 1325008187, 1325083914, 1326017288, 1328272543, 1328885292, 1331383316, + 1331593955, 1332379148, 1332467446, 1332620900, 1332937015, 1333790305, + 1334857284, 1335236478, 1335298265, 1335652095, 1335861008, 1336108161, + 1336513734, 1337406065, 1338090388, 1339293088, 1340949553, 1344125357, + 1345014111, 1345378476, 1345873587, 1345987999, 1347284277, 1348361229, + 1348373925, 1350653461, 1352833750, 1353144470, 1353999206, 1355086853, + 1355391150, 1356847855, 1361012693, 1361078114, 1365423458, 1367731096, + 1368235387, 1370528050, 1372169786, 1373978529, 1374125186, 1375470750, + 1377995642, 1379259015, 1379460673, 1379995885, 1382429941, 1382484003, + 1382551511, 1382893830, 1383307838, 1383640563, 1384314543, 1385675177, + 1386258142, 1386366954, 1387039848, 1387864976, 1388759892, 1389787451, + 1389954587, 1390040163, 1390274260, 1390306889, 1390477985, 1390656632, + 1391095995, 1391827090, 1392162662, 1394929399, 1395155215, 1395289708, + 1396662140, 1399673078, 1399854717, 1400070607, 1400929335, 1401058771, + 1401980800, 1402735006, 1403206383, 1403771494, 1404727477, 1405668251, + 1405763119, 1407094080, 1408805003, 1409001936, 1412540552, 1413860940, + 1414141069, 1414160501, 1415390230, 1417415680, 1417601340, 1420810050, + 1424797596, 1424859828, 1426483935, 1427504270, 1428223489, 1430821640, + 1431492783, 1431814151, 1434046375, 1434633879, 1435859758, 1437147036, + 1440145706, 1441811554, 1444048519, 1444076976, 1445225855, 1445279202, + 1445352642, 1447469119, 1447876480, 1450892344, 1451277631, 1451442832, + 1453397990, 1455316979, 1455756978, 1457464894, 1459100749, 1459138745, + 1459500136, 1459620801, 1462192184, 1462501905, 1462778368, 1462921857, + 1463269859, 1466632447, 1466679625, 1467376771, 1468486929, 1470285405, + 1471131758, 1471461419, 1471598258, 1472567100, 1473166451, 1473735214, + 1475638246, 1480273562, 1480324438, 1480437960, 1482983072, 1485740112, + 1486594282, 1487620835, 1489699243, 1491477359, 1492071476, 1492488573, + 1496078411, 1496277718, 1497680539, 1497926365, 1498554338, 1500102591, + 1501181424, 1502266386, 1502480836, 1502557594, 1504023310, 1505022272, + 1505475223, 1506716966, 1507433029, 1508705426, 1510133268, 1512135729, + 1512163910, 1513255537, 1514278798, 1514582723, 1514792012, 1515262458, + 1515528736, 1515933932, 1519017268, 1520667645, 1520886946, 1521350211, + 1522316172, 1524006589, 1524491858, 1525451290, 1527022962, 1528031307, + 1533447203, 1533496974, 1534330971, 1536213818, 1537459540, 1538069400, + 1538391713, 1539560507, 1541125975, 1541132933, 1541546082, 1545555937, + 1546057655, 1546104436, 1547662666, 1548588723, 1548804416, 1550012286, + 1550159640, 1550525411, 1550721460, 1551661799, 1553547892, 1554106660, + 1555325521, 1555660285, 1558244592, 1558695709, 1560142576, 1561212123, + 1561251584, 1561422777, 1561730727, 1563717683, 1564061243, 1565610678, + 1565700239, 1566878909, 1568176414, 1568395337, 1568900638, 1569479928, + 1570645300, 1571598133, 1572212863, 1575380740, 1577034674, 1577955754, + 1579725218, 1581062145, 1581118537, 1581801257, 1584408128, 1585050246, + 1588222158, 1588715179, 1589194592, 1590461855, 1590771096, 1593359038, + 1593837351, 1594621666, 1596042792, 1596821127, 1597506096, 1598436917, + 1599565995, 1599876594, 1600023202, 1600493361, 1601134240, 1601247294, + 1601617797, 1602517354, 1602827413, 1602967659, 1603428054, 1604330946, + 1604357658, 1605294199, 1605369007, 1607204293, 1607515830, 1607628402, + 1607677838, 1609955669, 1610244183, 1610539590, 1611500622, 1611558740, + 1611680320, 1612593993, 1612609979, 1614780688, 1617229086, 1618378831, + 1619598456, 1619641914, 1620562432, 1620803320, 1622692876, 1622758049, + 1624653209, 1627355806, 1628387880, 1628389501, 1629633514, 1629914848, + 1630293700, 1630776459, 1631405417, 1631935240, 1633512617, 1634445325, + 1635952299, 1636203720, 1636860706, 1640952769, 1642338733, 1642884452, + 1643991754, 1644353864, 1645969422, 1646704157, 1648575360, 1649344003, + 1649392096, 1650899832, 1652108025, 1653332243, 1655277291, 1656527154, + 1657001479, 1658037573, 1659079595, 1659137391, 1660678300, 1663100626, + 1663890071, 1665914525, 1666074170, 1667102296, 1667679197, 1672192305, + 1673314595, 1674045273, 1675300017, 1675494182, 1677449797, 1678967663, + 1679324299, 1679777458, 1680251429, 1680294422, 1681866717, 1682700783, + 1683915700, 1684018729, 1685350721, 1685880008, 1686215900, 1686479103, + 1686570783, 1687540458, 1687941280, 1688652157, 1689946986, 1690167792, + 1691486500, 1691676526, 1695191950, 1695617313, 1697686200, 1702425249, + 1704561346, 1705378439, 1705905941, 1706528514, 1708002892, 1708106609, + 1708126014, 1709019727, 1709697877, 1709823304, 1710305778, 1711569347, + 1711718534, 1711761015, 1711765426, 1712269139, 1712341869, 1713188647, + 1713475126, 1714295540, 1715279374, 1715379173, 1715402379, 1716853034, + 1719009954, 1719573683, 1721767511, 1723143470, 1725541486, 1726987516, + 1727583308, 1730333306, 1730721241, 1731949250, 1732028080, 1733366374, + 1733982669, 1737931879, 1738353358, 1740099311, 1740778973, 1740783613, + 1742032331, 1742434188, 1745247524, 1745393357, 1745543275, 1746273091, + 1746476698, 1746904676, 1747861289, 1749972876, 1750197960, 1750443194, + 1750955971, 1752088215, 1752318678, 1752598186, 1753125385, 1754447491, + 1755204784, 1757028186, 1758748737, 1760851607, 1761289401, 1763785295, + 1764436465, 1765354961, 1765830270, 1767380006, 1768795266, 1770497338, + 1771614266, 1771924274, 1772760484, 1774218355, 1774652096, 1774796872, + 1775331228, 1775608361, 1775805177, 1776471922, 1776794410, 1778544166, + 1781233744, 1781891230, 1783233173, 1783943137, 1784682542, 1787338655, + 1787391786, 1788389051, 1790557628, 1792756324, 1793190956, 1793747608, + 1797641070, 1799210492, 1800186189, 1800522575, 1802197319, 1802327778, + 1803183394, 1806061681, 1807085904, 1807172811, 1808237662, 1811088032, + 1815408878, 1816641611, 1817156134, 1820281480, 1821482472, 1821559709, + 1822279764, 1822612008, 1823053058, 1826590258, 1826628136, 1826880531, + 1827651881, 1833543700, 1833603743, 1834161399, 1834254978, 1834285819, + 1834318089, 1834388383, 1834852613, 1835203128, 1835860030, 1835977906, + 1836119534, 1837280287, 1838218199, 1839063567, 1840896199, 1846605728, + 1847908587, 1848681194, 1849495275, 1849657867, 1849675857, 1852181952, + 1855359256, 1858637614, 1858933377, 1859683061, 1860435620, 1860839427, + 1862876260, 1864952910, 1866094167, 1868151949, 1868289345, 1869264274, + 1870214195, 1870859970, 1871060346, 1871684562, 1873900475, 1874026715, + 1874855959, 1876255297, 1878253960, 1878434988, 1879635915, 1880483834, + 1880577483, 1881253854, 1883168929, 1883999260, 1886317932, 1890889527, + 1890938638, 1891630185, 1891904961, 1892370478, 1893095488, 1896952705, + 1897691227, 1897907456, 1898582764, 1900162831, 1900613055, 1901537567, + 1902501708, 1902595458, 1904243956, 1904514273, 1904576737, 1904770864, + 1905279500, 1906345724, 1907793490, 1908093581, 1909681194, 1910978116, + 1912065710, 1913489530, 1913581092, 1913926325, 1914764659, 1914809801, + 1915435344, 1916602098, 1918354829, 1920182565, 1924603748, 1926717418, + 1927367009, 1928309762, 1928550562, 1930662128, 1930880552, 1931282005, + 1931493432, 1932108837, 1933532372, 1933923245, 1935271536, 1936668087, + 1938258683, 1939683211, 1942089394, 1943137808, 1943651015, 1944679691, + 1945110368, 1946004194, 1948121717, 1949896838, 1950124297, 1951228823, + 1952177514, 1952364329, 1952805989, 1953450944, 1953919896, 1955436051, + 1957728250, 1957877704, 1958396887, 1959222232, 1959292396, 1961619988, + 1961647235, 1962689485, 1963053072, 1965358941, 1965855212, 1967705762, + 1968608155, 1968623233, 1969729134, 1970797151, 1971484382, 1971654864, + 1977865396, 1980923963, 1981481446, 1983379601, 1984143440, 1984152603, + 1985895474, 1986921372, 1989240516, 1992941115, 1994402695, 1997381015, + 2001056977, 2002881120, 2004664325, 2004770236, 2005021017, 2005653265, + 2005817027, 2006320950, 2006856683, 2009244921, 2010549018, 2011827638, + 2014090929, 2014129292, 2015847175, 2016161810, 2016464961, 2017191527, + 2018336444, 2022291557, 2022973269, 2023498746, 2024853642, 2027445678, + 2027814180, 2027828650, 2027909489, 2028116487, 2029133999, 2029909894, + 2033003588, 2033828953, 2034767768, 2036061488, 2036750157, 2037182006, + 2038925801, 2039012903, 2041942843, 2042861943, 2044016080, 2045407567, + 2045821218, 2046759770, 2047006719, 2047769687, 2048205721, 2048560397, + 2049698913, 2052321529, 2052328978, 2052343947, 2053899162, 2054212564, + 2055783490, 2060969286, 2061144988, 2063408339, 2064145552, 2064308502, + 2066440075, 2066865713, 2067100479, 2067499753, 2068343250, 2068743361, + 2069178089, 2070467093, 2072440819, 2073482870, 2074840378, 2074896270, + 2076612603, 2077667592, 2078528215, 2079492652, 2080735477, 2081544705, + 2082760612, 2084953915, 2085323316, 2087660971, 2088576982, 2088786920, + 2088955494, 2089981667, 2090025563, 2090543533, 2090570016, 2090684129, + 2091626028, 2093472294, 2094085507, 2094159153, 2094591332, 2094864173, + 2097057488, 2098829619, 2100497812, 2103236982, 2105321510, 2105638337, + 2105718728, 2105814934, 2108439398, 2109052886, 2109530615, 2109851473, + 2113434968, 2114587535, 2115445751, 2116495484, 2117880007, 2118203528, + 2118433006, 2118805956, 2118821565, 2119425672, 2122262571, 2122852959, + 2123379476, 2124749673, 2125958841, 2126029304, 2126940990, 2126963607, + 2128078174, 2128826472, 2129513521, 2129930580, 2130252471, 2130879949, + 2131761201, 2131797509, 2133066804, 2135673182, 2139976479, 2140091269, + 2140756121, 2141591317, 2142357746, 2143324534, 2143343312, 2143968639, + 2145073408, 2145930822}; diff --git a/test/SimTests/C/CImpl/qsort/qsort.c b/test/SimTests/C/CImpl/qsort/qsort.c new file mode 100644 index 0000000..e8ffebd --- /dev/null +++ b/test/SimTests/C/CImpl/qsort/qsort.c @@ -0,0 +1,179 @@ +// See LICENSE for license details. + +//************************************************************************** +// Quicksort benchmark +//-------------------------------------------------------------------------- +// +// This benchmark uses quicksort to sort an array of integers. The +// implementation is largely adapted from Numerical Recipes for C. The +// input data (and reference data) should be generated using the +// qsort_gendata.pl perl script and dumped to a file named +// dataset1.h. + +// The INSERTION_THRESHOLD is the size of the subarray when the +// algorithm switches to using an insertion sort instead of +// quick sort. + +typedef unsigned size_t; + +#define INSERTION_THRESHOLD 10 + +// NSTACK is the required auxiliary storage. +// It must be at least 2*lg(DATA_SIZE) + +#define NSTACK 50 + +//-------------------------------------------------------------------------- +// Input/Reference Data + +#define type int +#include "dataset1.h" + +// Swap macro for swapping two values. + +#define SWAP(a, b) \ + do { \ + typeof(a) temp = (a); \ + (a) = (b); \ + (b) = temp; \ + } while (0) +#define SWAP_IF_GREATER(a, b) \ + do { \ + if ((a) > (b)) \ + SWAP(a, b); \ + } while (0) + +//-------------------------------------------------------------------------- +// Quicksort function + +static void insertion_sort(size_t n, type arr[]) { + type *i, *j; + type value; + for (i = arr + 1; i < arr + n; i++) { + value = *i; + j = i; + while (value < *(j - 1)) { + *j = *(j - 1); + if (--j == arr) { + break; + } + } + *j = value; + } +} + +static void selection_sort(size_t n, type arr[]) { + for (type *i = arr; i < arr + n - 1; i++) { + for (type *j = i + 1; j < arr + n; j++) { + SWAP_IF_GREATER(*i, *j); + } + } +} + +void sort(size_t n, type arr[]) { + type *ir = arr + n; + type *l = arr + 1; + type *stack[NSTACK]; + type **stackp = stack; + + for (;;) { + // Insertion sort when subarray small enough. + if (ir - l < INSERTION_THRESHOLD) { + insertion_sort(ir - l + 1, l - 1); + + if (stackp == stack) { + break; + } + + // Pop stack and begin a new round of partitioning. + ir = *stackp--; + l = *stackp--; + } else { + // Choose median of left, center, and right elements as + // partitioning element a. Also rearrange so that a[l-1] <= a[l] <= + // a[ir-]. + SWAP(arr[((l - arr) + (ir - arr)) / 2 - 1], l[0]); + SWAP_IF_GREATER(l[-1], ir[-1]); + SWAP_IF_GREATER(l[0], ir[-1]); + SWAP_IF_GREATER(l[-1], l[0]); + + // Initialize pointers for partitioning. + type *i = l + 1; + type *j = ir; + + // Partitioning element. + type a = l[0]; + + for (;;) { // Beginning of innermost loop. + while (*i++ < a) + ; // Scan up to find element > a. + while (*(j-- - 2) > a) + ; // Scan down to find element < a. + if (j < i) { + break; // Pointers crossed. Partitioning complete. + } + SWAP(i[-1], j[-1]); // Exchange elements. + } // End of innermost loop. + + // Insert partitioning element. + l[0] = j[-1]; + j[-1] = a; + stackp += 2; + + // Push pointers to larger subarray on stack, + // process smaller subarray immediately. + + if (ir - i + 1 >= j - l) { + stackp[0] = ir; + stackp[-1] = i; + ir = j - 1; + } else { + stackp[0] = j - 1; + stackp[-1] = l; + l = i; + } + } + } +} + +//-------------------------------------------------------------------------- +// Main + +static int verify(int n, const int *test, const int *verify) { + int i; + // Unrolled for faster verification + for (i = 0; i < n / 2 * 2; i += 2) { + int t0 = test[i], t1 = test[i + 1]; + int v0 = verify[i], v1 = verify[i + 1]; + if (t0 != v0) { + return i + 1; + } + if (t1 != v1) { + return i + 2; + } + } + if (n % 2 != 0 && test[n - 1] != verify[n - 1]) { + return n; + } + return 0; +} + +int start() { +#if PREALLOCATE + // If needed we preallocate everything in the caches + sort(DATA_SIZE, verify_data); + if (verify(DATA_SIZE, input_data, input_data)) { + return 1; + } +#endif + + // Do the sort + // setStats(1); + sort(DATA_SIZE, input_data); + // setStats(0); + + // Check the results + return verify(DATA_SIZE, input_data, verify_data); +} + +int main() { start(); } diff --git a/test/SimTests/C/CImpl/xorshift32/CMakeLists.txt b/test/SimTests/C/CImpl/xorshift32/CMakeLists.txt new file mode 100644 index 0000000..39f717c --- /dev/null +++ b/test/SimTests/C/CImpl/xorshift32/CMakeLists.txt @@ -0,0 +1 @@ +protea_add_test(protea_test_xorshift32 main.c) diff --git a/test/SimTests/C/CImpl/xorshift32/main.c b/test/SimTests/C/CImpl/xorshift32/main.c new file mode 100644 index 0000000..d0da34e --- /dev/null +++ b/test/SimTests/C/CImpl/xorshift32/main.c @@ -0,0 +1,31 @@ +#include + +volatile uint32_t seed = 2463534242u; + +constexpr uint32_t N = 1024 * 1024; +uint32_t buffer[N]; + +uint32_t xorshift32(uint32_t x) { + x ^= x << 13; + x ^= x >> 17; + x ^= x << 5; + return x; +} + +uint32_t run() { + uint32_t x = seed; + + for (uint32_t i = 0; i < N; ++i) { + x = xorshift32(x); + buffer[i] = x; + } + + uint32_t sum = 0; + + for (uint32_t i = 0; i < N; ++i) + sum ^= buffer[i]; + + return sum; +} + +int main() { return run() == 752068848 ? 0 : 1; } diff --git a/test/SimTests/C/CImpl/zfunc/CMakeLists.txt b/test/SimTests/C/CImpl/zfunc/CMakeLists.txt new file mode 100644 index 0000000..bf117a9 --- /dev/null +++ b/test/SimTests/C/CImpl/zfunc/CMakeLists.txt @@ -0,0 +1 @@ +protea_add_test(protea_test_zfunc main.c) diff --git a/test/SimTests/C/CImpl/zfunc/main.c b/test/SimTests/C/CImpl/zfunc/main.c new file mode 100644 index 0000000..d91e382 --- /dev/null +++ b/test/SimTests/C/CImpl/zfunc/main.c @@ -0,0 +1,55 @@ +#define MAXN 1024 +const char *const str = "aBaBCAcabbBAababababcbCBCcbcBABbcbBbabacbBABcb"; + +int strlen(const char *str) { + int result = 0; + while (str[result] != '\0') + ++result; + return result; +} + +void z_function(const char *s, int n, int z[]) { + z[0] = n; + + int l = 0; + int r = 0; + + for (int i = 1; i < n; ++i) { + if (i <= r) { + int k = i - l; + int rem = r - i + 1; + z[i] = (z[k] < rem) ? z[k] : rem; + } else { + z[i] = 0; + } + + while (i + z[i] < n && s[z[i]] == s[i + z[i]]) + ++z[i]; + + if (i + z[i] - 1 > r) { + l = i; + r = i + z[i] - 1; + } + } +} + +int check_z_function(const char *s, int n, int z[]) { + + if (z[0] != n) + return 1; + for (int i = 1; i < n; ++i) { + int expected = 0; + while (i + expected < n && s[expected] == s[i + expected]) + ++expected; + if (z[i] != expected) + return 1; + } + return 0; +} + +int main() { + int z[MAXN]; + + z_function(str, strlen(str), z); + return check_z_function(str, strlen(str), z); +} diff --git a/test/SimTests/C/CMakeLists.txt b/test/SimTests/C/CMakeLists.txt new file mode 100644 index 0000000..b66f30a --- /dev/null +++ b/test/SimTests/C/CMakeLists.txt @@ -0,0 +1,38 @@ +project(c-simtests) + +if(PROTEA_BUILD_TESTS) + string(TOLOWER ${TARGET_NAME} TARGET_NAME_LOWER) + set(TARGET_TOOLCHAIN_FILE ${protea_SOURCE_DIR}/cmake/toolchain/${TARGET_NAME_LOWER}.cmake) + set(DEFAULT_ARGS + -DTARGET_NAME=${TARGET_NAME} + -DCMAKE_INSTALL_PREFIX:PATH=${CMAKE_INSTALL_PREFIX} + -DCMAKE_BUILD_TYPE:STRING=Release + -DCMAKE_TOOLCHAIN_FILE:PATH=${TARGET_TOOLCHAIN_FILE}) + if (DEFINED ${TARGET_NAME}_TOOLCHAIN_DIR) + list(APPEND DEFAULT_ARGS -D${TARGET_NAME}_TOOLCHAIN_DIR:PATH=${${TARGET_NAME}_TOOLCHAIN_DIR}) + endif() + + set(TESTS_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/CImpl) + set(OUTPUT_TESTS_DIR ${CMAKE_CURRENT_BINARY_DIR}/CImpl/bin) + set(GENERATED_SIM_BIN_PATH ${protea_simgen_BINARY_DIR}/sim) + + ExternalProject_Add( + c-simtestsimpl + SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/CImpl + BINARY_DIR ${TESTS_BINARY_DIR} + CONFIGURE_HANDLED_BY_BUILD True + BUILD_ALWAYS True + CMAKE_ARGS ${DEFAULT_ARGS} + INSTALL_COMMAND "") + + set(RunTests ${tests-common_SOURCE_DIR}/RunTests.py) + + set(DISCOVER_SCRIPT "${CMAKE_CURRENT_BINARY_DIR}/discover_generated_tests.cmake") + configure_file( + "${CMAKE_SOURCE_DIR}/cmake/discover_generated_tests.cmake.in" + "${DISCOVER_SCRIPT}" + @ONLY + ) + + set_property(DIRECTORY APPEND PROPERTY TEST_INCLUDE_FILES "${DISCOVER_SCRIPT}") +endif() diff --git a/test/SimTests/CMakeLists.txt b/test/SimTests/CMakeLists.txt new file mode 100644 index 0000000..6593b78 --- /dev/null +++ b/test/SimTests/CMakeLists.txt @@ -0,0 +1,9 @@ +project(simtests) + +add_subdirectory(C) +add_subdirectory(SnippetBased) + +if(PROTEA_BUILD_TESTS) + add_custom_target(${PROJECT_NAME}) + add_dependencies(${PROJECT_NAME} sim c-simtestsimpl snippetbased-simtestsimpl) +endif() diff --git a/test/SimTests/SnippetBased/CMakeLists.txt b/test/SimTests/SnippetBased/CMakeLists.txt new file mode 100644 index 0000000..7a837b8 --- /dev/null +++ b/test/SimTests/SnippetBased/CMakeLists.txt @@ -0,0 +1,41 @@ +project(snippetbased-simtests) + +if(PROTEA_BUILD_TESTS) + string(TOLOWER ${TARGET_NAME} TARGET_NAME_LOWER) + set(TARGET_TOOLCHAIN_FILE ${protea_SOURCE_DIR}/cmake/toolchain/${TARGET_NAME_LOWER}.cmake) + set(DEFAULT_ARGS + -DTARGET_NAME=${TARGET_NAME} + -DCMAKE_INSTALL_PREFIX:PATH=${CMAKE_INSTALL_PREFIX} + -DCMAKE_BUILD_TYPE:STRING=Release + -DCMAKE_TOOLCHAIN_FILE:PATH=${TARGET_TOOLCHAIN_FILE}) + if (DEFINED ${TARGET_NAME}_TOOLCHAIN_DIR) + list(APPEND DEFAULT_ARGS -D${TARGET_NAME}_TOOLCHAIN_DIR:PATH=${${TARGET_NAME}_TOOLCHAIN_DIR}) + endif() + + set(TESTS_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/SnippetBasedImpl) + set(OUTPUT_TESTS_DIR ${CMAKE_CURRENT_BINARY_DIR}/compiled_tests) + + set(RunTests ${tests-common_SOURCE_DIR}/RunTests.py) + set(GENERATED_SIM_BIN_PATH ${protea_simgen_BINARY_DIR}/sim) + + ExternalProject_Add( + snippetbased-simtestsimpl + SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/SnippetBasedImpl + BINARY_DIR ${TESTS_BINARY_DIR} + CONFIGURE_HANDLED_BY_BUILD True + BUILD_ALWAYS True + CMAKE_ARGS ${DEFAULT_ARGS} -DQEMU_PATH=${QEMU_PATH} -DBUNDLE_PATH=${BUNDLE_PATH} -DIR_PATH=${protea_irgen_BINARY_DIR}/IR.yaml -DTARGET_NAME=${TARGET_NAME} -DOUTPUT_TESTS_DIR=${OUTPUT_TESTS_DIR} + INSTALL_COMMAND "") + + add_dependencies(snippetbased-simtestsimpl protea_irgen sim) + + set(DISCOVER_SCRIPT "${CMAKE_CURRENT_BINARY_DIR}/discover_generated_tests.cmake") + configure_file( + "${CMAKE_SOURCE_DIR}/cmake/discover_generated_tests.cmake.in" + "${DISCOVER_SCRIPT}" + @ONLY + ) + + set_property(DIRECTORY APPEND PROPERTY TEST_INCLUDE_FILES "${DISCOVER_SCRIPT}") +endif() + diff --git a/test/SimTests/SnippetBased/SnippetBasedImpl/CMakeLists.txt b/test/SimTests/SnippetBased/SnippetBasedImpl/CMakeLists.txt new file mode 100644 index 0000000..432ba5e --- /dev/null +++ b/test/SimTests/SnippetBased/SnippetBasedImpl/CMakeLists.txt @@ -0,0 +1,54 @@ +cmake_minimum_required(VERSION 3.22 FATAL_ERROR) + +project(tests LANGUAGES CXX C ASM) + +set(INIT_DIR "${CMAKE_CURRENT_BINARY_DIR}/init") +set(BIN_DIR "${CMAKE_CURRENT_BINARY_DIR}/init_bin") +set(QEMU_LOG_DIR "${CMAKE_CURRENT_BINARY_DIR}/qemu_log") +set(OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/tests") + +add_custom_target(init-tests + ALL + COMMAND ${CMAKE_COMMAND} -E make_directory "${INIT_DIR}" + COMMAND ${BUNDLE_PATH} exec ruby ${CMAKE_CURRENT_SOURCE_DIR}/InitTestGen.rb -n 20 + ${IR_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/Target/${TARGET_NAME}/Snippets ${INIT_DIR} + COMMENT "Generating test .s files" +) + +set(COMPILER "${CMAKE_C_COMPILER}") +set(COMPILER_FLAGS + "${CMAKE_C_FLAGS} ${CMAKE_EXE_LINKER_FLAGS}" + CACHE INTERNAL "Compiler flags for assembling and linking" +) + +set(SCRIPT_PATH "${CMAKE_CURRENT_SOURCE_DIR}/compile-and-run.sh") +set(COMPILE_SCRIPT_PATH "${CMAKE_CURRENT_SOURCE_DIR}/compile-dir.sh") + +add_custom_target(run-qemu-tests + ALL + DEPENDS init-tests + COMMAND ${CMAKE_COMMAND} -E make_directory "${BIN_DIR}" + COMMAND ${CMAKE_COMMAND} -E make_directory "${QEMU_LOG_DIR}" + COMMAND "${SCRIPT_PATH}" + "${INIT_DIR}" + "${BIN_DIR}" + "${COMPILER}" + "${QEMU_PATH}" + "${QEMU_LOG_DIR}" + "${COMPILER_FLAGS}" + COMMENT "Compile all .s files and run each with QEMU" + VERBATIM +) + +add_custom_target(gen-tests + ALL + DEPENDS run-qemu-tests + COMMAND ${CMAKE_COMMAND} -E make_directory "${OUTPUT_DIR}" + COMMAND ${BUNDLE_PATH} exec ruby "${CMAKE_CURRENT_SOURCE_DIR}/TestGen.rb" + "${IR_PATH}" "${CMAKE_CURRENT_SOURCE_DIR}/Target/${TARGET_NAME}/Snippets" "${INIT_DIR}" "${QEMU_LOG_DIR}" "${OUTPUT_DIR}") + +add_custom_target(compile-tests + ALL + DEPENDS gen-tests + COMMAND ${CMAKE_COMMAND} -E make_directory "${OUTPUT_TESTS_DIR}" + COMMAND ${COMPILE_SCRIPT_PATH} "${OUTPUT_DIR}" "${OUTPUT_TESTS_DIR}" "${COMPILER}" "${COMPILER_FLAGS}") diff --git a/test/SimTests/SnippetBased/SnippetBasedImpl/InitTestGen.rb b/test/SimTests/SnippetBased/SnippetBasedImpl/InitTestGen.rb new file mode 100644 index 0000000..6d1aa4a --- /dev/null +++ b/test/SimTests/SnippetBased/SnippetBasedImpl/InitTestGen.rb @@ -0,0 +1,128 @@ +# frozen_string_literal: true + +require 'optparse' +require 'yaml' +require 'etc' +require 'parallel' +require 'Utility/gen_emitter' +require_relative 'Target/RISCV/Formatter' + +module SimTest + class InitAsmGen + attr_reader :ir, :reg2value, :rng + + def initialize(ir, seed: nil) + @ir = ir + @seed = seed || Random.new_seed + @rng = Random.new(@seed) + end + + def seed; @seed; end + + def generate_random_values + values = {} + ir[:regfiles].each do |regfile| + regfile[:regs].each do |reg| + attrs = reg[:attrs] || [] + next if attrs.include?(:zero) || attrs.include?(:pc) + size = reg[:size] || 32 + max = (1 << size) - 1 + values[reg[:name]] = @rng.rand(0..max) + end + end + values + end + + def generate(formatter, snippet_content, output_file) + emitter = Utility::GenEmitter.new() + @reg2value = generate_random_values + + formatter.prologue(emitter) + formatter.init_register(emitter, ir, @reg2value) + + emitter.emit_line 'j _snippet' + emitter.decrease_indent + emitter.emit_line '_snippet:' + emitter.increase_indent + formatter.insert_snippet(emitter, snippet_content) + emitter.emit_line 'j _exit_snippet' + emitter.decrease_indent + emitter.emit_line '_exit_snippet:' + emitter.increase_indent + + formatter.epilogue(emitter) + + File.write("#{output_file}yaml", @reg2value.to_yaml) + File.write("#{output_file}s", emitter.to_s) + end + end +end + +if __FILE__ == $PROGRAM_NAME + options = { + num_tests: 4, + seed: 42, + jobs: Etc.nprocessors + } + + parser = OptionParser.new do |opts| + opts.banner = "Usage: #{$PROGRAM_NAME} [options] " + + opts.on('-n', '--num-tests NUM', Integer, 'Number of tests per snippet (default: 4)') do |n| + options[:num_tests] = n + end + + opts.on('-s', '--seed SEED', Integer, 'Random seed for reproducibility') do |s| + options[:seed] = s + end + + opts.on('-j', '--jobs NUM', Integer, 'Number of parallel workers (default: CPU count)') do |j| + options[:jobs] = j + end + + opts.on('-h', '--help', 'Show this help message') do + puts opts + exit + end + end + + parser.parse! + + if ARGV.size != 3 + puts parser + exit 1 + end + + ir_file, snippets_dir, output_directory = ARGV + + ir = YAML.load_file(ir_file, symbolize_names: true) + + snippet_paths = Dir.glob(File.join(snippets_dir, '*')).select { |p| File.file?(p) } + + jobs = snippet_paths.each_with_index.flat_map do |snippet_path, snippet_idx| + snippet_content = File.read(snippet_path) + base = File.basename(snippet_path, '.*') + + options[:num_tests].times.map do |i| + { + ir: ir, + snippet_content: snippet_content, + base: base, + i: i, + num_tests: options[:num_tests], + seed: options[:seed] + snippet_idx * options[:num_tests] + i, + output_directory: output_directory + } + end + end + + Parallel.each(jobs, in_processes: options[:jobs]) do |job| + formatter = SimTest::RiscVFormatter.new + generator = SimTest::InitAsmGen.new(job[:ir], seed: job[:seed]) + + output_file = File.join(job[:output_directory], "#{job[:base]}_#{job[:i]}.init.") + generator.generate(formatter, job[:snippet_content], output_file) + + puts "Generated: #{output_file}s (seed: #{generator.seed}, test #{job[:i] + 1}/#{job[:num_tests]})" + end +end diff --git a/test/SimTests/SnippetBased/SnippetBasedImpl/Parsers/QEMUParser.rb b/test/SimTests/SnippetBased/SnippetBasedImpl/Parsers/QEMUParser.rb new file mode 100644 index 0000000..827e82a --- /dev/null +++ b/test/SimTests/SnippetBased/SnippetBasedImpl/Parsers/QEMUParser.rb @@ -0,0 +1,28 @@ +module SimTest + module QEMULogParser + # Parses a QEMU log file and returns the last register snapshot. + # The returned hash contains keys like "pc", "x0", "x1", ... "x31" + # with their 8‑digit hexadecimal values as strings. + def self.parse(log_file) + last_snapshot = nil + current_snapshot = nil + + File.readlines(log_file).each do |line| + line.strip! + next if line.empty? + if line.start_with?('pc') + last_snapshot = current_snapshot if current_snapshot + current_snapshot = {} + else + line.scan(/([^\s]+)\s+(\h+)/) do |name, value| + simple_name = name.split('/').first + current_snapshot[simple_name.to_sym] = value.to_i(16) + end + end + end + last_snapshot = current_snapshot if current_snapshot + + last_snapshot || {} + end + end +end diff --git a/test/SimTests/SnippetBased/SnippetBasedImpl/Target/RISCV/Formatter.rb b/test/SimTests/SnippetBased/SnippetBasedImpl/Target/RISCV/Formatter.rb new file mode 100644 index 0000000..7d53a43 --- /dev/null +++ b/test/SimTests/SnippetBased/SnippetBasedImpl/Target/RISCV/Formatter.rb @@ -0,0 +1,62 @@ +# frozen_string_literal: true + +module SimTest + class RiscVFormatter + def prologue(emitter) + emitter.emit_line '.global _start' + emitter.emit_line '.section .text' + emitter.emit_line '_start:' + emitter.increase_indent + end + + def init_register(emitter, ir, values) + ir[:regfiles].each do |regfile| + regfile[:regs].each do |reg| + attrs = reg[:attrs] || [] + next if attrs.include?(:zero) || attrs.include?(:pc) || reg[:name] == :x2 + + emitter.emit_line "li #{reg[:name]}, 0x#{values[reg[:name]].to_s(16)}" + end + end + end + + def insert_snippet(emitter, snippet_content) + snippet_content.each_line do |line| + emitter.emit_line line.chomp + end + end + + def insert_check(emitter, ir, ref_values) + emitter.emit_line 'addi sp, sp, -4' + emitter.emit_line 'sw x1, 4(sp)' + ir[:regfiles].each do |regfile| + regfile[:regs].each do |reg| + attrs = reg[:attrs] || [] + next if attrs.include?(:zero) || attrs.include?(:pc) || reg[:name] == :x1 || reg[:name] == :x2 + + emitter.emit_line "li x1, 0x#{ref_values[reg[:name]].to_s(16)}" + emitter.emit_line "bne x1, #{reg[:name]}, _fail" + end + end + emitter.emit_line 'lw x3, 4(sp)' + emitter.emit_line "li x1, 0x#{ref_values[:x1].to_s(16)}" + emitter.emit_line 'addi sp, sp, 4' + emitter.emit_line 'bne x1, x3, _fail' + end + + def epilogue(emitter) + emitter.emit_line 'li a0, 0' + emitter.emit_line 'li a7, 93' + emitter.emit_line 'ecall' + emitter.emit_blank_line + emitter.decrease_indent + emitter.emit_line '_fail:' + emitter.increase_indent + emitter.emit_line 'li a0, 1' + emitter.emit_line 'li a7, 93' + emitter.emit_line 'ecall' + emitter.decrease_indent + emitter.emit_blank_line + end + end +end diff --git a/test/SimTests/SnippetBased/SnippetBasedImpl/Target/RISCV/Snippets/add.s b/test/SimTests/SnippetBased/SnippetBasedImpl/Target/RISCV/Snippets/add.s new file mode 100644 index 0000000..b869fdb --- /dev/null +++ b/test/SimTests/SnippetBased/SnippetBasedImpl/Target/RISCV/Snippets/add.s @@ -0,0 +1,2 @@ +add x10, x6, x5 +add x12, x4, x15 diff --git a/test/SimTests/SnippetBased/SnippetBasedImpl/Target/RISCV/Snippets/addi.s b/test/SimTests/SnippetBased/SnippetBasedImpl/Target/RISCV/Snippets/addi.s new file mode 100644 index 0000000..aa94107 --- /dev/null +++ b/test/SimTests/SnippetBased/SnippetBasedImpl/Target/RISCV/Snippets/addi.s @@ -0,0 +1,2 @@ +addi x3, x6, 10 +addi x7, x12, 1000 diff --git a/test/SimTests/SnippetBased/SnippetBasedImpl/Target/RISCV/Snippets/and.s b/test/SimTests/SnippetBased/SnippetBasedImpl/Target/RISCV/Snippets/and.s new file mode 100644 index 0000000..f3301a1 --- /dev/null +++ b/test/SimTests/SnippetBased/SnippetBasedImpl/Target/RISCV/Snippets/and.s @@ -0,0 +1 @@ +and x20, x29, x23 diff --git a/test/SimTests/SnippetBased/SnippetBasedImpl/Target/RISCV/Snippets/andi.s b/test/SimTests/SnippetBased/SnippetBasedImpl/Target/RISCV/Snippets/andi.s new file mode 100644 index 0000000..b6f6a95 --- /dev/null +++ b/test/SimTests/SnippetBased/SnippetBasedImpl/Target/RISCV/Snippets/andi.s @@ -0,0 +1 @@ +andi x9, x8, -19 diff --git a/test/SimTests/SnippetBased/SnippetBasedImpl/Target/RISCV/Snippets/auipc.s b/test/SimTests/SnippetBased/SnippetBasedImpl/Target/RISCV/Snippets/auipc.s new file mode 100644 index 0000000..27d8080 --- /dev/null +++ b/test/SimTests/SnippetBased/SnippetBasedImpl/Target/RISCV/Snippets/auipc.s @@ -0,0 +1 @@ +auipc x17, 20 diff --git a/test/SimTests/SnippetBased/SnippetBasedImpl/Target/RISCV/Snippets/div.s b/test/SimTests/SnippetBased/SnippetBasedImpl/Target/RISCV/Snippets/div.s new file mode 100644 index 0000000..012b396 --- /dev/null +++ b/test/SimTests/SnippetBased/SnippetBasedImpl/Target/RISCV/Snippets/div.s @@ -0,0 +1 @@ +div x3, x20, x21 diff --git a/test/SimTests/SnippetBased/SnippetBasedImpl/Target/RISCV/Snippets/divu.s b/test/SimTests/SnippetBased/SnippetBasedImpl/Target/RISCV/Snippets/divu.s new file mode 100644 index 0000000..d4df7b2 --- /dev/null +++ b/test/SimTests/SnippetBased/SnippetBasedImpl/Target/RISCV/Snippets/divu.s @@ -0,0 +1 @@ +divu x16, x14, x12 diff --git a/test/SimTests/SnippetBased/SnippetBasedImpl/Target/RISCV/Snippets/factorial.s b/test/SimTests/SnippetBased/SnippetBasedImpl/Target/RISCV/Snippets/factorial.s new file mode 100644 index 0000000..c69dcb7 --- /dev/null +++ b/test/SimTests/SnippetBased/SnippetBasedImpl/Target/RISCV/Snippets/factorial.s @@ -0,0 +1,27 @@ + addi x10, x0, 10 + jal x1, factorial + j end +factorial: + addi x2, x2, -16 + + sw x1, 12(x2) + sw x10, 8(x2) + + addi x5, x0, 1 + ble x10, x5, base_case + addi x10, x10, -1 + jal x1, factorial + + lw x6, 8(x2) + mul x10, x10, x6 + jal x0, end_factorial + +base_case: + addi x10, x0, 1 + +end_factorial: + lw x1, 12(x2) + addi x2, x2, 16 + jalr x0, 0(x1) +end: + diff --git a/test/SimTests/SnippetBased/SnippetBasedImpl/Target/RISCV/Snippets/fib.s b/test/SimTests/SnippetBased/SnippetBasedImpl/Target/RISCV/Snippets/fib.s new file mode 100644 index 0000000..1e2ae25 --- /dev/null +++ b/test/SimTests/SnippetBased/SnippetBasedImpl/Target/RISCV/Snippets/fib.s @@ -0,0 +1,13 @@ + addi x5, x0, 0 # x5 = fib(0) + addi x6, x0, 1 # x6 = fib(1) + addi x7, x0, 15 # x7 = n + +.loop: + beq x7, x0, .end + + add x28, x5, x6 # x28 = x5 + x6 + addi x5, x6, 0 # x5 = x6 + addi x6, x28, 0 # x6 = x28 + addi x7, x7, -1 + jal x0, .loop +.end: diff --git a/test/SimTests/SnippetBased/SnippetBasedImpl/Target/RISCV/Snippets/lui.s b/test/SimTests/SnippetBased/SnippetBasedImpl/Target/RISCV/Snippets/lui.s new file mode 100644 index 0000000..c738390 --- /dev/null +++ b/test/SimTests/SnippetBased/SnippetBasedImpl/Target/RISCV/Snippets/lui.s @@ -0,0 +1 @@ +lui x10, 5 diff --git a/test/SimTests/SnippetBased/SnippetBasedImpl/Target/RISCV/Snippets/mul.s b/test/SimTests/SnippetBased/SnippetBasedImpl/Target/RISCV/Snippets/mul.s new file mode 100644 index 0000000..757fa79 --- /dev/null +++ b/test/SimTests/SnippetBased/SnippetBasedImpl/Target/RISCV/Snippets/mul.s @@ -0,0 +1 @@ +mul x8, x9, x10 diff --git a/test/SimTests/SnippetBased/SnippetBasedImpl/Target/RISCV/Snippets/mulh.s b/test/SimTests/SnippetBased/SnippetBasedImpl/Target/RISCV/Snippets/mulh.s new file mode 100644 index 0000000..e2166ab --- /dev/null +++ b/test/SimTests/SnippetBased/SnippetBasedImpl/Target/RISCV/Snippets/mulh.s @@ -0,0 +1 @@ +mulh x23, x24, x25 diff --git a/test/SimTests/SnippetBased/SnippetBasedImpl/Target/RISCV/Snippets/mulhsu.s b/test/SimTests/SnippetBased/SnippetBasedImpl/Target/RISCV/Snippets/mulhsu.s new file mode 100644 index 0000000..2d5498b --- /dev/null +++ b/test/SimTests/SnippetBased/SnippetBasedImpl/Target/RISCV/Snippets/mulhsu.s @@ -0,0 +1 @@ +mulhsu x23, x13, x29 diff --git a/test/SimTests/SnippetBased/SnippetBasedImpl/Target/RISCV/Snippets/mulhu.s b/test/SimTests/SnippetBased/SnippetBasedImpl/Target/RISCV/Snippets/mulhu.s new file mode 100644 index 0000000..5d58fe4 --- /dev/null +++ b/test/SimTests/SnippetBased/SnippetBasedImpl/Target/RISCV/Snippets/mulhu.s @@ -0,0 +1 @@ +mulhu x29, x27, x25 diff --git a/test/SimTests/SnippetBased/SnippetBasedImpl/Target/RISCV/Snippets/or.s b/test/SimTests/SnippetBased/SnippetBasedImpl/Target/RISCV/Snippets/or.s new file mode 100644 index 0000000..2d17681 --- /dev/null +++ b/test/SimTests/SnippetBased/SnippetBasedImpl/Target/RISCV/Snippets/or.s @@ -0,0 +1 @@ +or x1, x3, x7 diff --git a/test/SimTests/SnippetBased/SnippetBasedImpl/Target/RISCV/Snippets/ori.s b/test/SimTests/SnippetBased/SnippetBasedImpl/Target/RISCV/Snippets/ori.s new file mode 100644 index 0000000..e59d798 --- /dev/null +++ b/test/SimTests/SnippetBased/SnippetBasedImpl/Target/RISCV/Snippets/ori.s @@ -0,0 +1 @@ +ori x19, x9, -1 diff --git a/test/SimTests/SnippetBased/SnippetBasedImpl/Target/RISCV/Snippets/rem.s b/test/SimTests/SnippetBased/SnippetBasedImpl/Target/RISCV/Snippets/rem.s new file mode 100644 index 0000000..5269b50 --- /dev/null +++ b/test/SimTests/SnippetBased/SnippetBasedImpl/Target/RISCV/Snippets/rem.s @@ -0,0 +1 @@ +rem x4, x5, x6 diff --git a/test/SimTests/SnippetBased/SnippetBasedImpl/Target/RISCV/Snippets/remu.s b/test/SimTests/SnippetBased/SnippetBasedImpl/Target/RISCV/Snippets/remu.s new file mode 100644 index 0000000..429a7bd --- /dev/null +++ b/test/SimTests/SnippetBased/SnippetBasedImpl/Target/RISCV/Snippets/remu.s @@ -0,0 +1 @@ +remu x17, x11, x13 diff --git a/test/SimTests/SnippetBased/SnippetBasedImpl/Target/RISCV/Snippets/sll.s b/test/SimTests/SnippetBased/SnippetBasedImpl/Target/RISCV/Snippets/sll.s new file mode 100644 index 0000000..a09c353 --- /dev/null +++ b/test/SimTests/SnippetBased/SnippetBasedImpl/Target/RISCV/Snippets/sll.s @@ -0,0 +1 @@ +sll x9, x8, x7 diff --git a/test/SimTests/SnippetBased/SnippetBasedImpl/Target/RISCV/Snippets/slli.s b/test/SimTests/SnippetBased/SnippetBasedImpl/Target/RISCV/Snippets/slli.s new file mode 100644 index 0000000..2e5312f --- /dev/null +++ b/test/SimTests/SnippetBased/SnippetBasedImpl/Target/RISCV/Snippets/slli.s @@ -0,0 +1 @@ +slli x6, x7, 20 diff --git a/test/SimTests/SnippetBased/SnippetBasedImpl/Target/RISCV/Snippets/slt.s b/test/SimTests/SnippetBased/SnippetBasedImpl/Target/RISCV/Snippets/slt.s new file mode 100644 index 0000000..86ef33c --- /dev/null +++ b/test/SimTests/SnippetBased/SnippetBasedImpl/Target/RISCV/Snippets/slt.s @@ -0,0 +1 @@ +slt x19, x23, x28 diff --git a/test/SimTests/SnippetBased/SnippetBasedImpl/Target/RISCV/Snippets/slti.s b/test/SimTests/SnippetBased/SnippetBasedImpl/Target/RISCV/Snippets/slti.s new file mode 100644 index 0000000..0c05626 --- /dev/null +++ b/test/SimTests/SnippetBased/SnippetBasedImpl/Target/RISCV/Snippets/slti.s @@ -0,0 +1,2 @@ +slti x3, x3, 20 +slti x4, x6, 8 diff --git a/test/SimTests/SnippetBased/SnippetBasedImpl/Target/RISCV/Snippets/sltiu.s b/test/SimTests/SnippetBased/SnippetBasedImpl/Target/RISCV/Snippets/sltiu.s new file mode 100644 index 0000000..454a5e0 --- /dev/null +++ b/test/SimTests/SnippetBased/SnippetBasedImpl/Target/RISCV/Snippets/sltiu.s @@ -0,0 +1,2 @@ +sltiu x3, x3, 200 +sltiu x4, x6, 80 diff --git a/test/SimTests/SnippetBased/SnippetBasedImpl/Target/RISCV/Snippets/sltu.s b/test/SimTests/SnippetBased/SnippetBasedImpl/Target/RISCV/Snippets/sltu.s new file mode 100644 index 0000000..388a3bd --- /dev/null +++ b/test/SimTests/SnippetBased/SnippetBasedImpl/Target/RISCV/Snippets/sltu.s @@ -0,0 +1 @@ +sltu x19, x23, x28 diff --git a/test/SimTests/SnippetBased/SnippetBasedImpl/Target/RISCV/Snippets/sra.s b/test/SimTests/SnippetBased/SnippetBasedImpl/Target/RISCV/Snippets/sra.s new file mode 100644 index 0000000..6ec24f7 --- /dev/null +++ b/test/SimTests/SnippetBased/SnippetBasedImpl/Target/RISCV/Snippets/sra.s @@ -0,0 +1 @@ +sra x24, x22, x26 diff --git a/test/SimTests/SnippetBased/SnippetBasedImpl/Target/RISCV/Snippets/srai.s b/test/SimTests/SnippetBased/SnippetBasedImpl/Target/RISCV/Snippets/srai.s new file mode 100644 index 0000000..e04db3d --- /dev/null +++ b/test/SimTests/SnippetBased/SnippetBasedImpl/Target/RISCV/Snippets/srai.s @@ -0,0 +1 @@ +srai x7, x7, 20 diff --git a/test/SimTests/SnippetBased/SnippetBasedImpl/Target/RISCV/Snippets/srl.s b/test/SimTests/SnippetBased/SnippetBasedImpl/Target/RISCV/Snippets/srl.s new file mode 100644 index 0000000..4f38219 --- /dev/null +++ b/test/SimTests/SnippetBased/SnippetBasedImpl/Target/RISCV/Snippets/srl.s @@ -0,0 +1 @@ +srl x30, x29, x28 diff --git a/test/SimTests/SnippetBased/SnippetBasedImpl/Target/RISCV/Snippets/srli.s b/test/SimTests/SnippetBased/SnippetBasedImpl/Target/RISCV/Snippets/srli.s new file mode 100644 index 0000000..95bea0b --- /dev/null +++ b/test/SimTests/SnippetBased/SnippetBasedImpl/Target/RISCV/Snippets/srli.s @@ -0,0 +1 @@ +srli x11, x5, 2 diff --git a/test/SimTests/SnippetBased/SnippetBasedImpl/Target/RISCV/Snippets/sub.s b/test/SimTests/SnippetBased/SnippetBasedImpl/Target/RISCV/Snippets/sub.s new file mode 100644 index 0000000..32113ba --- /dev/null +++ b/test/SimTests/SnippetBased/SnippetBasedImpl/Target/RISCV/Snippets/sub.s @@ -0,0 +1 @@ +sub x7, x7, x20 diff --git a/test/SimTests/SnippetBased/SnippetBasedImpl/Target/RISCV/Snippets/xor.s b/test/SimTests/SnippetBased/SnippetBasedImpl/Target/RISCV/Snippets/xor.s new file mode 100644 index 0000000..b13d11e --- /dev/null +++ b/test/SimTests/SnippetBased/SnippetBasedImpl/Target/RISCV/Snippets/xor.s @@ -0,0 +1,2 @@ +xor x10, x6, x5 +xor x12, x4, x15 diff --git a/test/SimTests/SnippetBased/SnippetBasedImpl/Target/RISCV/Snippets/xori.s b/test/SimTests/SnippetBased/SnippetBasedImpl/Target/RISCV/Snippets/xori.s new file mode 100644 index 0000000..15fdebd --- /dev/null +++ b/test/SimTests/SnippetBased/SnippetBasedImpl/Target/RISCV/Snippets/xori.s @@ -0,0 +1 @@ +xori x5, x5, -1 diff --git a/test/SimTests/SnippetBased/SnippetBasedImpl/TestGen.rb b/test/SimTests/SnippetBased/SnippetBasedImpl/TestGen.rb new file mode 100644 index 0000000..1d274d2 --- /dev/null +++ b/test/SimTests/SnippetBased/SnippetBasedImpl/TestGen.rb @@ -0,0 +1,124 @@ +# frozen_string_literal: true + +require 'yaml' +require 'etc' +require 'parallel' +require 'optparse' +require 'Utility/gen_emitter' +require_relative 'Target/RISCV/Formatter' +require_relative 'Parsers/QEMUParser' + +module SimTest + class FinalAsmGen + attr_reader :ir + + def initialize(ir) + @ir = ir + end + + def generate(formatter, snippet_content, init_yaml_file, ref_log_file, output_file) + emitter = Utility::GenEmitter.new + + initreg2value = YAML.load_file(init_yaml_file) + refreg2value = QEMULogParser.parse(ref_log_file) + + formatter.prologue(emitter) + formatter.init_register(emitter, @ir, initreg2value) + + emitter.emit_line 'j _snippet' + emitter.decrease_indent + emitter.emit_line '_snippet:' + emitter.increase_indent + formatter.insert_snippet(emitter, snippet_content) + emitter.emit_line 'j _exit_snippet' + emitter.decrease_indent + emitter.emit_line '_exit_snippet:' + emitter.increase_indent + + formatter.insert_check(emitter, @ir, refreg2value) + + formatter.epilogue(emitter) + File.write(output_file, emitter.to_s) + end + end +end + +if __FILE__ == $0 + options = { + jobs: Etc.nprocessors + } + + parser = OptionParser.new do |opts| + opts.banner = "Usage: #{$0} [options] " + + opts.on('-j', '--jobs NUM', Integer, 'Number of parallel workers (default: CPU count)') do |j| + options[:jobs] = j + end + + opts.on('-h', '--help', 'Show this help message') do + puts opts + exit + end + end + + parser.parse! + + if ARGV.size != 5 + puts parser + exit 1 + end + + ir_file = ARGV[0] + snippets_dir = ARGV[1] + init_yaml_dir = ARGV[2] + log_dir = ARGV[3] + output_dir = ARGV[4] + + ir = YAML.load_file(ir_file, symbolize_names: true) + + snippet_map = Dir.glob(File.join(snippets_dir, '*')) + .select { |f| File.file?(f) } + .each_with_object({}) do |path, map| + map[File.basename(path, '.*')] = path + end + + log_files = Dir.glob(File.join(log_dir, '*')).select { |f| File.file?(f) } + + jobs = log_files.filter_map do |log_path| + log_basename = File.basename(log_path) + match = log_basename.match(/^(.*)_(\d+)\.init\.log$/) + + snippet = match[1] + index = match[2].to_i + + snippet_path = snippet_map[snippet] + + init_yaml_file = File.join(init_yaml_dir, "#{snippet}_#{index}.init.yaml") + + { + ir: ir, + snippet: snippet, + index: index, + snippet_path: snippet_path, + init_yaml_file: init_yaml_file, + log_path: log_path, + output_file: File.join(output_dir, "#{snippet}_#{index}.s") + } + end + + Parallel.each(jobs, in_processes: options[:jobs]) do |job| + generator = SimTest::FinalAsmGen.new(job[:ir]) + formatter = SimTest::RiscVFormatter.new + snippet_content = File.read(job[:snippet_path]) + + generator.generate( + formatter, + snippet_content, + job[:init_yaml_file], + job[:log_path], + job[:output_file] + ) + + puts "Generated: #{job[:output_file]}" + end +end diff --git a/test/SimTests/SnippetBased/SnippetBasedImpl/compile-and-run.sh b/test/SimTests/SnippetBased/SnippetBasedImpl/compile-and-run.sh new file mode 100755 index 0000000..7765bca --- /dev/null +++ b/test/SimTests/SnippetBased/SnippetBasedImpl/compile-and-run.sh @@ -0,0 +1,41 @@ +#!/bin/bash + +if [ $# -lt 6 ]; then + echo "Usage: $0 " + exit 1 +fi + +SRC_DIR="$1" +BIN_DIR="$2" +COMPILER="$3" +QEMU="$4" +QEMU_LOG_DIR="$5" +COMPILER_FLAGS="$6" + +# Compile each .s file +for s_file in "$SRC_DIR"/*.s; do + if [ -f "$s_file" ]; then + base=$(basename "$s_file" .s) + out_file="$BIN_DIR/$base.elf" + echo "Compiling $s_file -> $out_file" + $COMPILER $COMPILER_FLAGS "$s_file" -o "$out_file" + if [ $? -ne 0 ]; then + echo "Compilation failed for $s_file" + exit 1 + fi + fi +done + +# Run QEMU on each compiled binary +for bin_file in "$BIN_DIR"/*.elf; do + if [ -f "$bin_file" ]; then + echo "Running QEMU on $bin_file" + base_name="${bin_file##*/}" + base_name="${base_name%.elf}" + $QEMU -D "$QEMU_LOG_DIR/$base_name.log" -d cpu "$bin_file" + if [ $? -ne 0 ]; then + echo "QEMU failed on $bin_file" + exit 1 + fi + fi +done diff --git a/test/SimTests/SnippetBased/SnippetBasedImpl/compile-dir.sh b/test/SimTests/SnippetBased/SnippetBasedImpl/compile-dir.sh new file mode 100755 index 0000000..2968116 --- /dev/null +++ b/test/SimTests/SnippetBased/SnippetBasedImpl/compile-dir.sh @@ -0,0 +1,24 @@ +#!/bin/bash + +if [ $# -ne 4 ]; then + echo "Usage: $0 " + exit 1 +fi + +SRC_DIR="$1" +BIN_DIR="$2" +COMPILER="$3" +COMPILER_FLAGS="$4" + +for s_file in "$SRC_DIR"/*.s; do + if [ -f "$s_file" ]; then + base=$(basename "$s_file" .s) + out_file="$BIN_DIR/$base.elf" + echo "Compiling $s_file -> $out_file" + $COMPILER $COMPILER_FLAGS "$s_file" -o "$out_file" + if [ $? -ne 0 ]; then + echo "Compilation failed for $s_file" + exit 1 + fi + fi +done diff --git a/tests/SemaTests/simple_tests.rb b/tests/SemaTests/simple_tests.rb deleted file mode 100644 index 506c0d1..0000000 --- a/tests/SemaTests/simple_tests.rb +++ /dev/null @@ -1,94 +0,0 @@ -require 'code_gen/code_gen' -require 'Utility/gen_emitter' -require 'minitest/autorun' - -class SemaTestsSimple < Minitest::Test - def test_add_instruction - operation = { - name: 'add', - oprnds: [ - { name: 'rd', type: 's32', regset: nil }, - { name: 'rs1', type: 's32', regset: nil }, - { name: 'rs2', type: 's32', regset: nil } - ], - attrs: nil - } - - mapping = { - 'rd' => 'R[rd]', - 'rs1' => 'R[rs1]', - 'rs2' => 'R[rs2]' - } - - expected_code = 'R[rd] = R[rs1] + R[rs2];' - emitter = Utility::GenEmitter.new - generated_code = CodeGen.new(emitter, mapping) - generated_code.generate_statement(operation) - - assert_equal(expected_code, emitter.to_s) - end - - def test_let_instruction - operation = { - name: 'let', - oprnds: [ - { name: 'rd', type: 's32', regset: nil }, - { name: nil, type: 'iconst', value: 42 } - ], - attrs: nil - } - - mapping = { - 'rd' => 'R[rd]' - } - - expected_code = 'R[rd] = 42;' - emitter = Utility::GenEmitter.new - generated_code = CodeGen.new(emitter, mapping) - generated_code.generate_statement(operation) - - assert_equal(expected_code, emitter.to_s) - end - - def test_new_var_instruction - operation = { - name: 'new_var', - oprnds: [ - { name: 'temp', type: 's32', regset: nil } - ], - attrs: nil - } - - mapping = {} - - expected_code = 'int32_t temp;' - emitter = Utility::GenEmitter.new - generated_code = CodeGen.new(emitter, mapping) - generated_code.generate_statement(operation) - - assert_equal(expected_code, emitter.to_s) - end - - def test_cast_instruction - operation = { - name: 'cast', - oprnds: [ - { name: 'rd', type: 's32', regset: nil }, - { name: 'rs', type: 'u16', regset: nil } - ], - attrs: nil - } - - mapping = { - 'rd' => 'R[rd]', - 'rs' => 'R[rs]' - } - - expected_code = 'R[rd] = (static_cast(R[rs]) << 16) >> 16;' - emitter = Utility::GenEmitter.new - generated_code = CodeGen.new(emitter, mapping) - generated_code.generate_statement(operation) - - assert_equal(expected_code, emitter.to_s) - end -end