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.
- checksums.yaml +7 -0
- data/.gitignore +13 -0
- data/.rspec +2 -0
- data/.rubocop.yml +38 -0
- data/.travis.yml +4 -0
- data/.yardopts +1 -0
- data/CODE_OF_CONDUCT.md +49 -0
- data/Gemfile +11 -0
- data/LICENSE.txt +21 -0
- data/README.md +41 -0
- data/Rakefile +14 -0
- data/carbon.gemspec +30 -0
- data/lib/carbon.rb +54 -0
- data/lib/carbon/concrete.rb +43 -0
- data/lib/carbon/concrete/build.rb +63 -0
- data/lib/carbon/concrete/index.rb +324 -0
- data/lib/carbon/concrete/item.rb +37 -0
- data/lib/carbon/concrete/item/base.rb +153 -0
- data/lib/carbon/concrete/item/data.rb +22 -0
- data/lib/carbon/concrete/item/function.rb +97 -0
- data/lib/carbon/concrete/item/internal.rb +83 -0
- data/lib/carbon/concrete/item/struct.rb +65 -0
- data/lib/carbon/concrete/item/struct/element.rb +42 -0
- data/lib/carbon/concrete/item/trait.rb +72 -0
- data/lib/carbon/concrete/item/trait/expectation.rb +55 -0
- data/lib/carbon/concrete/request.rb +137 -0
- data/lib/carbon/concrete/type.rb +260 -0
- data/lib/carbon/concrete/type/function.rb +91 -0
- data/lib/carbon/concrete/type/generic.rb +118 -0
- data/lib/carbon/concrete/type/name.rb +147 -0
- data/lib/carbon/concrete/type/parse.rb +172 -0
- data/lib/carbon/concrete/type/part.rb +100 -0
- data/lib/carbon/core.rb +61 -0
- data/lib/carbon/core/int.rb +87 -0
- data/lib/carbon/core/integer.rb +109 -0
- data/lib/carbon/core/integer/cast.rb +83 -0
- data/lib/carbon/core/integer/math.rb +198 -0
- data/lib/carbon/core/integer/misc.rb +145 -0
- data/lib/carbon/core/integer/pole.rb +133 -0
- data/lib/carbon/core/integer/ship.rb +71 -0
- data/lib/carbon/core/integer/sign.rb +52 -0
- data/lib/carbon/core/integer/type.rb +42 -0
- data/lib/carbon/core/integer/zero.rb +52 -0
- data/lib/carbon/core/pointer.rb +54 -0
- data/lib/carbon/core/pointer/access.rb +123 -0
- data/lib/carbon/core/pointer/cast.rb +55 -0
- data/lib/carbon/core/pointer/math.rb +187 -0
- data/lib/carbon/core/pointer/memory.rb +85 -0
- data/lib/carbon/core/pointer/type.rb +23 -0
- data/lib/carbon/tacky.rb +21 -0
- data/lib/carbon/tacky/block.rb +96 -0
- data/lib/carbon/tacky/builder.rb +310 -0
- data/lib/carbon/tacky/context.rb +66 -0
- data/lib/carbon/tacky/function.rb +137 -0
- data/lib/carbon/tacky/instruction.rb +170 -0
- data/lib/carbon/tacky/parameter.rb +23 -0
- data/lib/carbon/tacky/reference.rb +23 -0
- data/lib/carbon/tacky/value.rb +40 -0
- data/lib/carbon/version.rb +9 -0
- 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
|