furnace 0.3.1 → 0.4.0.beta.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. data/.gitignore +1 -0
  2. data/.travis.yml +4 -0
  3. data/Gemfile +1 -2
  4. data/{LICENSE → LICENSE.MIT} +14 -14
  5. data/Rakefile +1 -5
  6. data/furnace.gemspec +7 -3
  7. data/lib/furnace/ast/node.rb +14 -0
  8. data/lib/furnace/ast/processor.rb +7 -7
  9. data/lib/furnace/ssa/argument.rb +23 -0
  10. data/lib/furnace/ssa/basic_block.rb +117 -0
  11. data/lib/furnace/ssa/builder.rb +100 -0
  12. data/lib/furnace/ssa/constant.rb +43 -0
  13. data/lib/furnace/ssa/function.rb +191 -0
  14. data/lib/furnace/ssa/generic_instruction.rb +14 -0
  15. data/lib/furnace/ssa/generic_type.rb +16 -0
  16. data/lib/furnace/ssa/instruction.rb +92 -0
  17. data/lib/furnace/ssa/instruction_syntax.rb +109 -0
  18. data/lib/furnace/ssa/instructions/branch.rb +11 -0
  19. data/lib/furnace/ssa/instructions/phi.rb +63 -0
  20. data/lib/furnace/ssa/instructions/return.rb +11 -0
  21. data/lib/furnace/ssa/module.rb +52 -0
  22. data/lib/furnace/ssa/named_value.rb +25 -0
  23. data/lib/furnace/ssa/pretty_printer.rb +113 -0
  24. data/lib/furnace/ssa/terminator_instruction.rb +24 -0
  25. data/lib/furnace/ssa/type.rb +27 -0
  26. data/lib/furnace/ssa/types/basic_block.rb +11 -0
  27. data/lib/furnace/ssa/types/function.rb +11 -0
  28. data/lib/furnace/ssa/types/void.rb +15 -0
  29. data/lib/furnace/ssa/user.rb +84 -0
  30. data/lib/furnace/ssa/value.rb +62 -0
  31. data/lib/furnace/ssa.rb +66 -0
  32. data/lib/furnace/transform/iterative.rb +27 -0
  33. data/lib/furnace/transform/pipeline.rb +3 -3
  34. data/lib/furnace/version.rb +1 -1
  35. data/lib/furnace.rb +3 -3
  36. data/test/ast_test.rb +32 -3
  37. data/test/ssa_test.rb +1129 -0
  38. data/test/test_helper.rb +17 -28
  39. data/test/transform_test.rb +74 -0
  40. metadata +136 -58
  41. data/lib/furnace/cfg/algorithms.rb +0 -193
  42. data/lib/furnace/cfg/graph.rb +0 -99
  43. data/lib/furnace/cfg/node.rb +0 -78
  44. data/lib/furnace/cfg.rb +0 -7
  45. data/lib/furnace/transform/iterative_process.rb +0 -26
@@ -0,0 +1,92 @@
1
+ module Furnace
2
+ class SSA::Instruction < SSA::User
3
+ def self.opcode
4
+ @opcode ||= SSA.class_name_to_opcode(self)
5
+ end
6
+
7
+ def self.syntax(&block)
8
+ SSA::InstructionSyntax.new(self).evaluate(&block)
9
+ end
10
+
11
+ attr_accessor :basic_block
12
+
13
+ def initialize(basic_block, operands=[], name=nil)
14
+ super(basic_block.function, operands, name)
15
+ @basic_block = basic_block
16
+ end
17
+
18
+ def initialize_copy(original)
19
+ super
20
+
21
+ @operands = nil
22
+ end
23
+
24
+ def opcode
25
+ self.class.opcode
26
+ end
27
+
28
+ def remove
29
+ @basic_block.remove self
30
+ detach
31
+ end
32
+
33
+ def replace_with(value)
34
+ replace_all_uses_with value
35
+
36
+ if value.is_a? SSA::Instruction
37
+ @basic_block.replace self, value
38
+ detach
39
+ else
40
+ remove
41
+ end
42
+ end
43
+
44
+ def has_side_effects?
45
+ false
46
+ end
47
+
48
+ def terminator?
49
+ false
50
+ end
51
+
52
+ def pretty_print(p=SSA::PrettyPrinter.new)
53
+ unless type == SSA.void
54
+ p.type type
55
+ p.name name
56
+ p.text '='
57
+ end
58
+
59
+ if valid?
60
+ p.keyword opcode
61
+ else
62
+ p.keyword_invalid opcode
63
+ end
64
+
65
+ pretty_parameters(p)
66
+ pretty_operands(p)
67
+
68
+ p
69
+ end
70
+
71
+ def inspect_as_value(p=SSA::PrettyPrinter.new)
72
+ if type == SSA.void
73
+ p.type type
74
+ else
75
+ super
76
+ end
77
+ end
78
+
79
+ protected
80
+
81
+ def pretty_parameters(p)
82
+ end
83
+
84
+ def pretty_operands(p)
85
+ if @operands
86
+ p.values @operands
87
+ else
88
+ p.text '<DETACHED>'
89
+ end
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,109 @@
1
+ module Furnace
2
+ class SSA::InstructionSyntax
3
+ def initialize(klass)
4
+ @klass = klass
5
+ @operands = {}
6
+ @splat = nil
7
+ end
8
+
9
+ def evaluate
10
+ yield self
11
+
12
+ codegen
13
+ end
14
+
15
+ def operand(name, type=nil)
16
+ check_for_splat
17
+
18
+ type = type.to_type unless type.nil?
19
+ @operands[name.to_sym] = type
20
+ end
21
+
22
+ def splat(name)
23
+ check_for_splat
24
+
25
+ @splat = name.to_sym
26
+ end
27
+
28
+ protected
29
+
30
+ def check_for_splat
31
+ if @splat
32
+ raise ArgumentError, "There should be at most one splat operand in tail position"
33
+ end
34
+ end
35
+
36
+ def codegen
37
+ operands, splat = @operands, @splat
38
+
39
+ @klass.class_eval do
40
+ operands.each_with_index do |(operand, type), index|
41
+ define_method(operand) do
42
+ @operands[index]
43
+ end
44
+
45
+ define_method(:"#{operand}=") do |value|
46
+ value = value.to_value
47
+
48
+ @operands[index].remove_use self if @operands[index]
49
+ @operands[index] = value
50
+ value.add_use self if value
51
+
52
+ value
53
+ end
54
+ end
55
+
56
+ if splat
57
+ define_method splat do
58
+ @operands[operands.size..-1]
59
+ end
60
+
61
+ define_method(:"#{splat}=") do |values|
62
+ values = values.map(&:to_value)
63
+
64
+ update_use_lists do
65
+ @operands[operands.size, @operands.size - operands.size] = values
66
+ end
67
+
68
+ values
69
+ end
70
+ end
71
+
72
+ define_method(:operands=) do |values|
73
+ if splat && values.size < operands.size
74
+ raise ArgumentError, "Not enough operands provided: #{values.size} for #{operands.size}"
75
+ elsif !splat && values.size != operands.size
76
+ raise ArgumentError, "Incorrect number of operands provided: #{values.size} for #{operands.size}"
77
+ end
78
+
79
+ values = values.map(&:to_value)
80
+
81
+ update_use_lists do
82
+ @operands = values
83
+ end
84
+
85
+ verify!
86
+
87
+ values
88
+ end
89
+
90
+ define_method(:verify!) do |ignore_nil_types=true|
91
+ return if @operands.nil?
92
+
93
+ operands.each_with_index do |(operand, type), index|
94
+ next if type.nil?
95
+
96
+ value = send operand
97
+ next if ignore_nil_types && value.type.nil?
98
+
99
+ if value.type.nil? || !value.type.subtype_of?(type)
100
+ raise TypeError, "Wrong type for operand ##{index + 1} `#{operand}': #{SSA.inspect_type type} is expected, #{SSA.inspect_type value.type} is present"
101
+ end
102
+ end
103
+
104
+ nil
105
+ end
106
+ end
107
+ end
108
+ end
109
+ end
@@ -0,0 +1,11 @@
1
+ module Furnace
2
+ class SSA::BranchInsn < SSA::TerminatorInstruction
3
+ syntax do |s|
4
+ s.operand :target, SSA::BasicBlock
5
+ end
6
+
7
+ def exits?
8
+ false
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,63 @@
1
+ module Furnace
2
+ class SSA::PhiInsn < SSA::GenericInstruction
3
+ def initialize(basic_block, type, operands={}, name=nil)
4
+ super(basic_block, type, operands, name)
5
+ end
6
+
7
+ def each_operand(&block)
8
+ return to_enum(:each_operand) if block.nil?
9
+
10
+ if @operands
11
+ @operands.each do |basic_block, value|
12
+ yield basic_block
13
+ yield value
14
+ end
15
+ end
16
+ end
17
+
18
+ def operands=(operands)
19
+ update_use_lists do
20
+ @operands = operands
21
+ end
22
+ end
23
+
24
+ def translate_operands(map)
25
+ Hash[@operands.map do |basic_block, value|
26
+ [ map[basic_block], map[value] ]
27
+ end]
28
+ end
29
+
30
+ protected
31
+
32
+ def pretty_operands(p)
33
+ @operands.each_with_index do |(basic_block, value), index|
34
+ p.name basic_block.name
35
+ p.text '=>'
36
+ value.inspect_as_value p
37
+
38
+ p << ',' if index < @operands.count - 1
39
+ end
40
+ end
41
+
42
+ def replace_uses_of_operands(use, new_use)
43
+ if @operands.include? use
44
+ value = @operands[use]
45
+ @operands.delete use
46
+ @operands[new_use] = value
47
+
48
+ true
49
+ else
50
+ found = false
51
+
52
+ @operands.each do |basic_block, operand|
53
+ if operand == use
54
+ found = true
55
+ @operands[basic_block] = new_use
56
+ end
57
+ end
58
+
59
+ found
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,11 @@
1
+ module Furnace
2
+ class SSA::ReturnInsn < SSA::TerminatorInstruction
3
+ syntax do |s|
4
+ s.operand :value
5
+ end
6
+
7
+ def exits?
8
+ true
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,52 @@
1
+ module Furnace
2
+ class SSA::Module
3
+ def initialize
4
+ @functions = {}
5
+ @next_id = 0
6
+ end
7
+
8
+ def to_a
9
+ @functions.values
10
+ end
11
+
12
+ def each(&block)
13
+ @functions.values.each(&block)
14
+ end
15
+
16
+ def include?(name)
17
+ @functions.include? name
18
+ end
19
+
20
+ def [](name)
21
+ unless @functions.include? name
22
+ raise ArgumentError, "function #{name} is not found"
23
+ end
24
+
25
+ @functions[name]
26
+ end
27
+
28
+ def add(function, name_prefix=nil)
29
+ if name_prefix ||
30
+ function.name.nil? ||
31
+ @functions.include?(function.name)
32
+
33
+ basename = name_prefix || function.name || 'function'
34
+ basename = basename.gsub /;\d+$/, ''
35
+
36
+ function.name = "#{basename};#{make_id}"
37
+ end
38
+
39
+ @functions[function.name] = function
40
+ end
41
+
42
+ def remove(name)
43
+ @functions.delete name
44
+ end
45
+
46
+ protected
47
+
48
+ def make_id
49
+ @next_id += 1
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,25 @@
1
+ module Furnace
2
+ class SSA::NamedValue < SSA::Value
3
+ attr_accessor :function
4
+ attr_reader :name
5
+
6
+ def initialize(function, name)
7
+ super()
8
+
9
+ @function = function
10
+ self.name = name
11
+ end
12
+
13
+ def name=(name)
14
+ @name = @function.make_name(name)
15
+ end
16
+
17
+ def inspect_as_value(p=SSA::PrettyPrinter.new)
18
+ p.name name
19
+ end
20
+
21
+ def inspect
22
+ pretty_print
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,113 @@
1
+ require 'ansi'
2
+
3
+ module Furnace
4
+ class SSA::PrettyPrinter
5
+ @colorize = true
6
+
7
+ class << self
8
+ attr_accessor :colorize
9
+ end
10
+
11
+ def initialize(colorize=self.class.colorize)
12
+ @colorize = colorize
13
+ @buffer = ""
14
+ @need_space = false
15
+
16
+ yield self if block_given?
17
+ end
18
+
19
+ def to_s
20
+ @buffer
21
+ end
22
+
23
+ alias to_str to_s
24
+
25
+ def ==(other)
26
+ to_s == other
27
+ end
28
+
29
+ def =~(other)
30
+ to_s =~ other
31
+ end
32
+
33
+ def <<(what)
34
+ @buffer << what
35
+
36
+ self
37
+ end
38
+
39
+ def text(*what)
40
+ what = what.map(&:to_s)
41
+ return if what.all?(&:empty?)
42
+
43
+ ensure_space do
44
+ self << what.join
45
+ end
46
+ end
47
+
48
+ def newline
49
+ @need_space = false
50
+
51
+ self << "\n"
52
+ end
53
+
54
+ def name(what)
55
+ text '%', what
56
+ end
57
+
58
+ def type(what)
59
+ text with_ansi(:green) { Furnace::SSA.inspect_type(what) }
60
+ end
61
+
62
+ def keyword(what)
63
+ text with_ansi(:bright, :white) { what.to_s }
64
+ end
65
+
66
+ def keyword_invalid(what)
67
+ if @colorize
68
+ text with_ansi(:bright, :red) { what.to_s }
69
+ else
70
+ text "!#{what}".to_s
71
+ end
72
+ end
73
+
74
+ def objects(objects, separator=",", printer=:pretty_print)
75
+ objects.each_with_index do |object, index|
76
+ object.send(printer, self)
77
+
78
+ self << separator if index < objects.count - 1
79
+ end
80
+
81
+ self
82
+ end
83
+
84
+ def values(values, separator=",")
85
+ objects(values, separator, :inspect_as_value)
86
+ end
87
+
88
+ protected
89
+
90
+ def with_ansi(*colors)
91
+ string = yield
92
+
93
+ if @colorize
94
+ ANSI::Code.ansi(yield, *colors)
95
+ else
96
+ yield
97
+ end
98
+ end
99
+
100
+ def ensure_space(need_space_after=true)
101
+ if @need_space
102
+ self << " "
103
+ @need_space = false
104
+ end
105
+
106
+ yield
107
+
108
+ @need_space = need_space_after
109
+
110
+ self
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,24 @@
1
+ module Furnace
2
+ class SSA::TerminatorInstruction < SSA::Instruction
3
+ def has_side_effects?
4
+ true
5
+ end
6
+
7
+ def terminator?
8
+ true
9
+ end
10
+
11
+ def exits?
12
+ raise NotImplementedError, "reimplement SSA::TerminatorInstruction#exits? in a subclass"
13
+ end
14
+
15
+ def successors
16
+ operands.
17
+ select do |value|
18
+ value.type == SSA::BasicBlockType.instance
19
+ end.map do |value|
20
+ value.name
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,27 @@
1
+ module Furnace
2
+ class SSA::Type
3
+ def to_type
4
+ self
5
+ end
6
+
7
+ def monotype?
8
+ true
9
+ end
10
+
11
+ def ==(other)
12
+ other.instance_of?(self.class)
13
+ end
14
+
15
+ def hash
16
+ [self.class].hash
17
+ end
18
+
19
+ def eql?(other)
20
+ self == other
21
+ end
22
+
23
+ def subtype_of?(other)
24
+ self == other
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,11 @@
1
+ module Furnace
2
+ class SSA::BasicBlockType < SSA::Type
3
+ def self.instance
4
+ @instance ||= new
5
+ end
6
+
7
+ def inspect
8
+ "label"
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ module Furnace
2
+ class SSA::FunctionType < SSA::Type
3
+ def self.instance
4
+ @instance ||= new
5
+ end
6
+
7
+ def inspect
8
+ "function"
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,15 @@
1
+ module Furnace
2
+ class SSA::VoidType < SSA::Type
3
+ def inspect
4
+ 'void'
5
+ end
6
+
7
+ def self.instance
8
+ @instance ||= new
9
+ end
10
+
11
+ def self.value
12
+ @value ||= SSA::Constant.new(instance, nil)
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,84 @@
1
+ module Furnace
2
+ class SSA::User < SSA::NamedValue
3
+ attr_reader :operands
4
+
5
+ def initialize(function, operands=[], name=nil)
6
+ super(function, name)
7
+
8
+ self.operands = operands
9
+ end
10
+
11
+ def each_operand(&block)
12
+ @operands.each &block if @operands
13
+ end
14
+
15
+ def operands=(operands)
16
+ update_use_lists do
17
+ @operands = operands.map(&:to_value)
18
+ end
19
+ end
20
+
21
+ def detach
22
+ update_use_lists do
23
+ @operands = nil
24
+ end
25
+ end
26
+
27
+ def translate_operands(map)
28
+ @operands.map do |operand|
29
+ map[operand]
30
+ end
31
+ end
32
+
33
+ def replace_uses_of(value, new_value)
34
+ if replace_uses_of_operands(value, new_value)
35
+ value.remove_use(self)
36
+ new_value.add_use(self)
37
+ else
38
+ raise ArgumentError, "#{value.inspect} is not used in #{self.inspect}"
39
+ end
40
+
41
+ self
42
+ end
43
+
44
+ def valid?(*args)
45
+ verify!(*args)
46
+ true
47
+ rescue TypeError
48
+ false
49
+ end
50
+
51
+ def verify!
52
+ # do nothing
53
+ end
54
+
55
+ protected
56
+
57
+ def update_use_lists
58
+ each_operand do |operand|
59
+ operand.remove_use(self)
60
+ end
61
+
62
+ value = yield
63
+
64
+ each_operand do |operand|
65
+ operand.add_use(self)
66
+ end
67
+
68
+ value
69
+ end
70
+
71
+ def replace_uses_of_operands(value, new_value)
72
+ found = false
73
+
74
+ @operands.each_with_index do |operand, index|
75
+ if operand == value
76
+ found = true
77
+ @operands[index] = new_value
78
+ end
79
+ end
80
+
81
+ found
82
+ end
83
+ end
84
+ end