carbon-core 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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