carbon-core 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|