module Ikra::Translator
This module contains functionality for translating Ruby code to CUDA (C++) code.
Constants
- BlockSelectorDummy
Public Class Methods
Reads a CUDA source code file and replaces identifiers by provided substitutes. @param [String] file_name name of source code file @param [Hash{String => String}] replacements replacements
# File lib/translator/translator.rb, line 55 def read_file(file_name:, replacements: {}) full_name = Ikra::Configuration.resource_file_name(file_name) if !File.exist?(full_name) raise AssertionError.new("File does not exist: #{full_name}") end contents = File.open(full_name, "rb").read replacements.each do |s1, s2| replacement = "/*{#{s1}}*/" contents = contents.gsub(replacement, s2) end contents end
Translates a Ruby block to CUDA source code. @param [AST::BlockDefNode] block_def_node AST
(abstract syntax tree) of the block @param [EnvironmentBuilder] environment_builder environment builder instance collecting information about lexical variables (environment) @param [Array{Variable}] block_parameters types and names of parameters to the block @param [Hash{Symbol => Object}] lexical_variables all lexical variables that are accessed within the block @param [Fixnum] command_id a unique identifier of the block @param [String] pre_execution source code that should be run before executing the block @param [Array{Variable}] override_block_parameters overrides the the declaration of parameters that this block accepts. @param [EntireInputTranslationResult] entire_input_translation The result of `translate_entire_input` @return [BlockTranslationResult]
# File lib/translator/block_translator.rb, line 54 def translate_block( block_def_node:, environment_builder:, command_id:, lexical_variables: {}, # One one of the two following parameter configurations is valid: # a) Either this parameter is given: entire_input_translation: nil, # b) or these parameters are given (some are optional): pre_execution: nil, override_block_parameters: nil, block_parameters: nil) # Check and prepare arguments if pre_execution != nil and entire_input_translation != nil raise ArgumentError.new("pre_execution and entire_input_translation given") elsif entire_input_translation != nil pre_execution = entire_input_translation.pre_execution elsif pre_execution == nil pre_execution = "" end if block_parameters != nil and entire_input_translation != nil raise ArgumentError.new("block_parameters and entire_input_translation given") elsif entire_input_translation != nil block_parameters = entire_input_translation.block_parameters elsif block_parameters == nil block_parameters = [] end if override_block_parameters != nil and entire_input_translation != nil raise ArgumentError.new("override_block_parameters and entire_input_translation given") elsif entire_input_translation != nil override_block_parameters = entire_input_translation.override_block_parameters elsif override_block_parameters == nil override_block_parameters = block_parameters end # Build hash of parameter name -> type mappings block_parameter_types = {} for variable in block_parameters block_parameter_types[variable.name] = variable.type end parameter_types_string = "[" + block_parameter_types.map do |id, type| "#{id}: #{type}" end.join(", ") + "]" Log.info("Translating block with input types #{parameter_types_string}") # Add information to block_def_node block_def_node.parameters_names_and_types = block_parameter_types # Lexical variables lexical_variables.each do |name, value| block_def_node.lexical_variables_names_and_types[name] = value.ikra_type.to_union_type end # Type inference type_inference_visitor = TypeInference::Visitor.new return_type = type_inference_visitor.process_block(block_def_node) # Translation to source code ast_translator = ASTTranslator.new # Auxiliary methods are instance methods that are called by the block aux_methods = type_inference_visitor.all_methods.map do |method| ast_translator.translate_method(method) end # Generate method predeclarations aux_methods_predecl = type_inference_visitor.all_methods.map do |method| ast_translator.translate_method_predecl(method) end # Start with predeclarations aux_methods = aux_methods_predecl + aux_methods # Classify variables (lexical or local) block_def_node.accept(VariableClassifier.new( lexical_variable_names: lexical_variables.keys)) # Translate to CUDA/C++ code translation_result = ast_translator.translate_block(block_def_node) # Load environment variables lexical_variables.each do |name, value| type = value.ikra_type mangled_name = environment_builder.add_object(name, value) translation_result.prepend("#{type.to_c_type} #{Constants::LEXICAL_VAR_PREFIX}#{name} = #{Constants::ENV_IDENTIFIER}->#{mangled_name};\n") end # Declare local variables block_def_node.local_variables_names_and_types.each do |name, type| translation_result.prepend("#{type.to_c_type} #{name};\n") end # Function signature mangled_name = "_block_k_#{command_id}_" function_parameters = ["environment_t *#{Constants::ENV_IDENTIFIER}"] parameter_decls = override_block_parameters.map do |variable| "#{variable.type.to_c_type} #{variable.name}" end function_parameters.push(*parameter_decls) translation_result = Translator.read_file( file_name: "block_function_head.cpp", replacements: { "name" => mangled_name, "result_type" => return_type.to_c_type, "parameters" => function_parameters.join(", "), "body" => wrap_in_c_block(pre_execution + "\n" + translation_result)}) # TODO: handle more than one result type return BlockTranslationResult.new( c_source: translation_result, result_type: return_type, function_name: mangled_name, aux_methods: aux_methods) end
# File lib/translator/translator.rb, line 48 def wrap_in_c_block(str) "{\n" + str.split("\n").map do |line| " " + line end.join("\n") + "\n}\n" end