carbon-core 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (60) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +13 -0
  3. data/.rspec +2 -0
  4. data/.rubocop.yml +38 -0
  5. data/.travis.yml +4 -0
  6. data/.yardopts +1 -0
  7. data/CODE_OF_CONDUCT.md +49 -0
  8. data/Gemfile +11 -0
  9. data/LICENSE.txt +21 -0
  10. data/README.md +41 -0
  11. data/Rakefile +14 -0
  12. data/carbon.gemspec +30 -0
  13. data/lib/carbon.rb +54 -0
  14. data/lib/carbon/concrete.rb +43 -0
  15. data/lib/carbon/concrete/build.rb +63 -0
  16. data/lib/carbon/concrete/index.rb +324 -0
  17. data/lib/carbon/concrete/item.rb +37 -0
  18. data/lib/carbon/concrete/item/base.rb +153 -0
  19. data/lib/carbon/concrete/item/data.rb +22 -0
  20. data/lib/carbon/concrete/item/function.rb +97 -0
  21. data/lib/carbon/concrete/item/internal.rb +83 -0
  22. data/lib/carbon/concrete/item/struct.rb +65 -0
  23. data/lib/carbon/concrete/item/struct/element.rb +42 -0
  24. data/lib/carbon/concrete/item/trait.rb +72 -0
  25. data/lib/carbon/concrete/item/trait/expectation.rb +55 -0
  26. data/lib/carbon/concrete/request.rb +137 -0
  27. data/lib/carbon/concrete/type.rb +260 -0
  28. data/lib/carbon/concrete/type/function.rb +91 -0
  29. data/lib/carbon/concrete/type/generic.rb +118 -0
  30. data/lib/carbon/concrete/type/name.rb +147 -0
  31. data/lib/carbon/concrete/type/parse.rb +172 -0
  32. data/lib/carbon/concrete/type/part.rb +100 -0
  33. data/lib/carbon/core.rb +61 -0
  34. data/lib/carbon/core/int.rb +87 -0
  35. data/lib/carbon/core/integer.rb +109 -0
  36. data/lib/carbon/core/integer/cast.rb +83 -0
  37. data/lib/carbon/core/integer/math.rb +198 -0
  38. data/lib/carbon/core/integer/misc.rb +145 -0
  39. data/lib/carbon/core/integer/pole.rb +133 -0
  40. data/lib/carbon/core/integer/ship.rb +71 -0
  41. data/lib/carbon/core/integer/sign.rb +52 -0
  42. data/lib/carbon/core/integer/type.rb +42 -0
  43. data/lib/carbon/core/integer/zero.rb +52 -0
  44. data/lib/carbon/core/pointer.rb +54 -0
  45. data/lib/carbon/core/pointer/access.rb +123 -0
  46. data/lib/carbon/core/pointer/cast.rb +55 -0
  47. data/lib/carbon/core/pointer/math.rb +187 -0
  48. data/lib/carbon/core/pointer/memory.rb +85 -0
  49. data/lib/carbon/core/pointer/type.rb +23 -0
  50. data/lib/carbon/tacky.rb +21 -0
  51. data/lib/carbon/tacky/block.rb +96 -0
  52. data/lib/carbon/tacky/builder.rb +310 -0
  53. data/lib/carbon/tacky/context.rb +66 -0
  54. data/lib/carbon/tacky/function.rb +137 -0
  55. data/lib/carbon/tacky/instruction.rb +170 -0
  56. data/lib/carbon/tacky/parameter.rb +23 -0
  57. data/lib/carbon/tacky/reference.rb +23 -0
  58. data/lib/carbon/tacky/value.rb +40 -0
  59. data/lib/carbon/version.rb +9 -0
  60. metadata +186 -0
@@ -0,0 +1,66 @@
1
+ # encoding: utf-8
2
+ # frozen_string_literal: true
3
+
4
+ require "forwardable"
5
+
6
+ module Carbon
7
+ module Tacky
8
+ # A "context." This contains all of the information needed to build a
9
+ # function, such as instruction values (From LLVM), block mapping,
10
+ # parameters, and the build ({Concrete::Build}).
11
+ #
12
+ # @api private
13
+ class Context
14
+ extend Forwardable
15
+ # The values of instructions. This is used to properly map
16
+ # {Tacky::Reference} values to the proper `LLVM::Value`s.
17
+ #
18
+ # @return [<::LLVM::Value>]
19
+ attr_reader :instructions
20
+
21
+ # The mapping for blocks. This is used by instructions to map a
22
+ # {Tacky::Block} to the proper `LLVM::BasicBlock`, since many llvm
23
+ # instructions take basic blocks as parameters.
24
+ #
25
+ # @return [{Tacky::Block => ::LLVM::BasicBlock}]
26
+ attr_reader :blocks
27
+
28
+ # The generics that the function is being built with.
29
+ #
30
+ # @return [{::String => Concrete::Type}]
31
+ attr_reader :generics
32
+
33
+ # The parameters that are passed from the function. This is used to
34
+ # convert {Tacky::Parameter} references to `LLVM::Value`s.
35
+ #
36
+ # @return [<::LLVM::Value>]
37
+ attr_reader :params
38
+
39
+ # The actual build.
40
+ #
41
+ # @return [Concrete::Build]
42
+ attr_reader :build
43
+
44
+ # (see Concrete::Build#types)
45
+ attr_reader :types
46
+ def_delegator :@build, :types
47
+
48
+ # (see Concrete::Build#functions)
49
+ attr_reader :functions
50
+ def_delegator :@build, :functions
51
+
52
+ # Initialize the context.
53
+ #
54
+ # @param build [Concrete::Build]
55
+ # @param generics [{::String => Concrete::Type}]
56
+ def initialize(build, generics)
57
+ @build = build
58
+ @generics = generics
59
+ @instructions = []
60
+ @blocks = {}
61
+ @params = []
62
+ freeze
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,137 @@
1
+ # encoding: utf-8
2
+ # frozen_string_literal: true
3
+
4
+ module Carbon
5
+ module Tacky
6
+ # A pseudo function. This is used for definitions in order to better
7
+ # serialize them. However, each instruction uses a {Concrete::Type}
8
+ # instead of the LLVM type that it corresponds to, allowing expansion
9
+ # only upon generation.
10
+ #
11
+ # @api semiprivate
12
+ class Function
13
+ # A mutable counter for giving out instruction ids. This makes it so
14
+ # that _all_ instruction ids are unique for every function.
15
+ #
16
+ # @api private
17
+ class Counter
18
+ # The value.
19
+ # @return [::Numeric]
20
+ attr_reader :value
21
+
22
+ # Initialize the counter.
23
+ #
24
+ # @param value [::Numeric] The initial value of the counter.
25
+ def initialize(value = 0)
26
+ @value = value
27
+ end
28
+
29
+ # Increments the counter by one, and returns the new value.
30
+ #
31
+ # @return [::Numeric]
32
+ def increment
33
+ @value += 1
34
+ end
35
+ end
36
+
37
+ # The basic blocks of the function. The first block is expected to be
38
+ # the entry block; the rest can be in any order.
39
+ #
40
+ # @return [<Tacky::Block>]
41
+ attr_reader :blocks
42
+
43
+ # The instruction id counter.
44
+ #
45
+ # @return [Counter]
46
+ attr_reader :counter
47
+
48
+ # Creates a function with the given parameters and blocks. The
49
+ # parameters are required; the blocks should not be given.
50
+ #
51
+ # @param parameters [<Concrete::Type>] The parameters that are passed
52
+ # to the function.
53
+ # @param blocks [<Tacky::Block>] Should not be used.
54
+ def initialize(parameters, blocks = [])
55
+ @blocks = blocks
56
+ @parameters = parameters
57
+ @counter = Counter.new
58
+ params
59
+ freeze
60
+ build(&Proc.new) if block_given?
61
+ end
62
+
63
+ # Creates a new {Tacky::Block} with the given name, adding it to the
64
+ # block list, and returns it.
65
+ #
66
+ # @param name [::String] The name of the new block.
67
+ # @return [Tacky::Block] The new block.
68
+ def add(name = "")
69
+ block = Block.new(self, name)
70
+ @blocks << block
71
+ block
72
+ end
73
+
74
+ # Finds the block with the given name, if it exists. Note that multiple
75
+ # blocks can have the same name and still be disparate.
76
+ #
77
+ # @param name [::String] The name of the block to find.
78
+ # @return [Tacky::Block] If the block can be found.
79
+ # @return [nil] Otherwise.
80
+ def find(name)
81
+ @blocks.find { |b| b.name == name }
82
+ end
83
+
84
+ # Returns a set of dependencies that the function depends on. The set
85
+ # is built by asking the constituant blocks for their dependencies,
86
+ # and merging them all into one set. If no blocks exist, or there are
87
+ # no dependencies, an empty set is returned.
88
+ #
89
+ # @api private
90
+ # @return [Set<Concrete::Type>] The dependencies of the function.
91
+ def dependencies
92
+ @blocks.map(&:dependencies).inject(Set.new, :merge)
93
+ end
94
+
95
+ # Creates the function for LLVM. This has three main steps: first, mark
96
+ # the function parameters with names. Second, create the function blocks
97
+ # for instructional use. Third, call the constituant blocks for them
98
+ # to build themselves.
99
+ #
100
+ # @api private
101
+ # @param function [::LLVM::Function] The LLVM function.
102
+ # @param build [Concrete::Build] The build. This should contain all
103
+ # relevant information for building this function.
104
+ # @param generics [{::String => Concrete::Type}] The generics that are
105
+ # being applied to the function.
106
+ # @return [void]
107
+ def call(function, build, generics)
108
+ context = Context.new(build, generics)
109
+ mark_function_params(context, function)
110
+
111
+ @blocks.each do |block|
112
+ context.blocks[block] = function.basic_blocks.append(block.name)
113
+ end.each { |block| block.call(context) }
114
+ end
115
+
116
+ # The parameters of the function. These are enclosed with a
117
+ # {Tacky::Parameter} to allow the name of said parameters to be set.
118
+ # The parameters are given an index and a type.
119
+ #
120
+ # @return [Tacky::Parameter]
121
+ def params
122
+ @params ||= @parameters.each_with_index.map do |type, i|
123
+ Tacky::Parameter.new(i, type)
124
+ end
125
+ end
126
+
127
+ private
128
+
129
+ def mark_function_params(context, function)
130
+ params.zip(function.params).each do |(param, arg)|
131
+ arg.name = param.name
132
+ context.params << arg
133
+ end
134
+ end
135
+ end
136
+ end
137
+ end
@@ -0,0 +1,170 @@
1
+ # encoding: utf-8
2
+ # frozen_string_literal: true
3
+
4
+ module Carbon
5
+ module Tacky
6
+ # An instruction. This is the building block of the rest of the Tacky
7
+ # structures. Each instruction performs some sort of action.
8
+ class Instruction < Value
9
+ # Instructions that can be represented by this class that can have a
10
+ # name parameter.
11
+ #
12
+ # @return [<::String>]
13
+ NAMED_INSTRUCTIONS = %w(
14
+ add alloca and array_alloca array_malloc ashr bit_cast exact_sdiv
15
+ extract_element extract_value fadd fcmp fdiv fmul fp2si fp2ui fp_cast
16
+ fp_ext fp_trunc frem fsub gep global_string global_string_pointer icmp
17
+ inbounds_gep insert_element insert_value int2ptr int_cast invoke
18
+ is_not_null is_null load lshr malloc mul neg not nsw_add nsw_mul
19
+ nsw_neg nsw_sub nuw_add nuw_mul nuw_neg nuw_sub or phi pointer_cast
20
+ ptr2int ptr_diff sdiv select sext sext_or_bit_cast shl shuffle_vector
21
+ si2fp srem struct_gep sub trunc trunc_or_bit_cast udiv ui2fp urem xor
22
+ zext zext_or_bit_cast
23
+ ).freeze
24
+
25
+ # The name of the instruction. This is limited to a specific set; see
26
+ # the method names on {Tacky::Builder} for what they can be.
27
+ #
28
+ # @return [::String]
29
+ attr_reader :instruction
30
+
31
+ # The parameters of the instruction.
32
+ #
33
+ # @return [<Reference, Parameter, Value, ::Object>]
34
+ attr_reader :parameters
35
+
36
+ # Initializes the instruction with the given id, name, and parameters.
37
+ #
38
+ # @param id [::Numeric] The ID of the instruction. This is the number
39
+ # of the instruction in the block.
40
+ # @param name [::Symbol, ::String] The name of the instruction.
41
+ # @param parameters [<Reference, Parameter, Value, ::Object>] The
42
+ # parameters to be used with the instruction.
43
+ def initialize(id, name, parameters)
44
+ @value = id
45
+ @instruction = name.to_s
46
+ @parameters = parameters
47
+ end
48
+
49
+ # Builds the instruction. If the instruction is special, i.e. it starts
50
+ # with an underscore, it is handled seperately. Otherwise, it is handled
51
+ # normally.
52
+ #
53
+ # @api private
54
+ # @see #generate_null
55
+ # @see #generate_sizeof
56
+ # @see #generate_normal
57
+ # @param context [Tacky::Context] The context.
58
+ # @param block [::LLVM::BasicBlock] The block.
59
+ # @return [LLVM::Value]
60
+ def call(context, block)
61
+ case @instruction
62
+ when "_null" then generate_null(context, block)
63
+ when "_sizeof" then generate_sizeof(context, block)
64
+ else generate_normal(context, block)
65
+ end
66
+ end
67
+
68
+ # The dependencies of the instruction. If any of the parameters are a
69
+ # {Concrete::Type}, it is set as a dependency, and the result is put into
70
+ # a set and returned.
71
+ #
72
+ # @api private
73
+ # @return [Set<Concrete::Type>]
74
+ def dependencies
75
+ @parameters.select { |p| p.is_a?(Concrete::Type) }.inject(Set.new, :<<)
76
+ end
77
+
78
+ # Retrieves the name of the instruction. The name of the instruction
79
+ # is essentally the name used by the return value that represents the
80
+ # instruction. If this instruction is not a named instruction (i.e.
81
+ # {#instruction} is not in {NAMED_INSTRUCTIONS}), or the last parameter
82
+ # isn't a string, it returns an empty string (`""`). Otherwise, it
83
+ # returns the last parameter.
84
+ #
85
+ # @return [::String] The name of the instruction.
86
+ def name
87
+ if NAMED_INSTRUCTIONS.include?(@instruction) &&
88
+ @parameters.last.is_a?(::String)
89
+ @parameters.last
90
+ else
91
+ ""
92
+ end
93
+ end
94
+
95
+ # Sets the name of the instruction. The name of the instruction is
96
+ # essentially the name used by the return value that represents the
97
+ # instruction. If this instruction is not a named instruction (i.e.
98
+ # {#instruction} is not in {NAMED_INSTRUCTIONS}), then this does nothing.
99
+ # If the last parameter is a string, this sets the last parameter to the
100
+ # new name. Otherwise, it adds the name to the end of the parameters
101
+ # list.
102
+ #
103
+ # @param name [::String] The new name of the instruction.
104
+ # @return [::String] The new name of the instruction.
105
+ def name=(name)
106
+ return name unless NAMED_INSTRUCTIONS.include?(@instruction)
107
+ if @parameters.last.is_a?(::String)
108
+ @parameters[-1] = name
109
+ else
110
+ @parameters << name
111
+ end
112
+
113
+ name
114
+ end
115
+
116
+ private
117
+
118
+ # Generates a "normal" instruction. It maps the parameters, then sends
119
+ # the instruction over to a builder for LLVM, before returning the
120
+ # result of the instruction.
121
+ #
122
+ # @param context [Tacky::Context] The context.
123
+ # @param block [::LLVM::BasicBlock] The block.
124
+ # @return [::LLVM::Value] The result of the llvm instruction.
125
+ def generate_normal(context, block)
126
+ params = mapped_parameters(context, block)
127
+ value = nil
128
+
129
+ block.build { |b| value = b.public_send(@instruction, *params) }
130
+ value
131
+ end
132
+
133
+ def generate_null(context, block)
134
+ params = mapped_parameters(context, block)
135
+ context.types.fetch(params.first).last.null
136
+ end
137
+
138
+ def generate_sizeof(context, block)
139
+ params = mapped_parameters(context, block)
140
+ context.types.fetch(params.first).last.size
141
+ end
142
+
143
+ def mapped_parameters(context, block)
144
+ @parameters.map { |p| mapped_parameter(p, context, block) }
145
+ end
146
+
147
+ # rubocop:disable Metrics/CyclomaticComplexity
148
+ def mapped_parameter(param, context, _block)
149
+ case param
150
+ when Concrete::Type then map_parameter_type(param, context)
151
+ when Tacky::Reference then context.instructions.fetch(param.id)
152
+ when Tacky::Parameter then context.params.fetch(param.value)
153
+ when ::Integer then LLVM.Int(param)
154
+ when ::Float then LLVM.Float(param)
155
+ when ::String then param
156
+ else fail ArgumentError, "Unexpected parameter #{param.class}"
157
+ end
158
+ end
159
+ # rubocop:enable Metrics/CyclomaticComplexity
160
+
161
+ def map_parameter_type(param, context)
162
+ if param.function?
163
+ context.functions.fetch(param)
164
+ else
165
+ context.types.fetch(param).last
166
+ end
167
+ end
168
+ end
169
+ end
170
+ end
@@ -0,0 +1,23 @@
1
+ # encoding: utf-8
2
+ # frozen_string_literal: true
3
+
4
+ module Carbon
5
+ module Tacky
6
+ # A parameter that's been passed in by a function. This just contains the
7
+ # number, name, and type of the parameter, for proper mapping later.
8
+ class Parameter < Value
9
+ # Initializes the parameter with the given number, name, and type.
10
+ #
11
+ # @param number [::Numeric] The number of the parameter. The far left
12
+ # of a parameter list is zero (0).
13
+ # @param type [Concrete::Type] The type of the parameter.
14
+ # @param name [::String] The name of the parameter. If one is not given,
15
+ # the name is autogenerated.
16
+ def initialize(number, type, name = "")
17
+ @name = name
18
+ @type = type
19
+ @value = number
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,23 @@
1
+ # encoding: utf-8
2
+ # frozen_string_literal: true
3
+
4
+ module Carbon
5
+ module Tacky
6
+ # A "reference." A reference is essentially a mapping to an instruction
7
+ # that returned a value, and the reference takes the place of said value.
8
+ # The reference represents the return value of an instruction.
9
+ class Reference
10
+ # The id of the instruction that this reference points to.
11
+ #
12
+ # @return [::Numeric]
13
+ attr_reader :id
14
+
15
+ # Initializes the reference with the given ID.
16
+ #
17
+ # @param id [::Numeric] The ID.
18
+ def initialize(id)
19
+ @id = id
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,40 @@
1
+ # encoding: utf-8
2
+ # frozen_string_literal: true
3
+
4
+ module Carbon
5
+ module Tacky
6
+ # A "value." A value can be a parameter, a reference, or a number.
7
+ #
8
+ # @todo TODO: Figure out if I still need this.
9
+ class Value
10
+ # The name of the value. This can be set in order to make the resulting
11
+ # LLVM IR pretty.
12
+ #
13
+ # @return [::String]
14
+ attr_accessor :name
15
+
16
+ # The type of the value. This is used in order to determine typing
17
+ # information for LLVM.
18
+ #
19
+ # @return [Concrete::Type] If it is set.
20
+ # @return [nil] If it is not set or determinable.
21
+ attr_accessor :type
22
+
23
+ # The actual value.
24
+ #
25
+ # @return [::Object]
26
+ attr_accessor :value
27
+
28
+ # Initialize the value.
29
+ #
30
+ # @param name [::String] The name of the value.
31
+ # @param type [Concrete::Type, nil] The type of the value.
32
+ # @param value [::Object] The actual value.
33
+ def initialize(name, type, value)
34
+ @name = name
35
+ @type = type
36
+ @value = value
37
+ end
38
+ end
39
+ end
40
+ end