-
Notifications
You must be signed in to change notification settings - Fork 12
/
main.go
147 lines (122 loc) · 3.95 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
// This code was presented at Sheffield Go, March 7th 2019. See README.md for
// more explanation and link to slides. It is enough to demonstrate the essence
// of a basic compiler which works by taking the Go SSA and translating it to
// LLVM IR. Lots of functionality is intentionally missing.
package main
import (
"fmt"
"os"
goconstant "go/constant"
ir "github.com/llir/llvm/ir"
irconstant "github.com/llir/llvm/ir/constant"
irtypes "github.com/llir/llvm/ir/types"
irvalue "github.com/llir/llvm/ir/value"
"golang.org/x/tools/go/packages"
"golang.org/x/tools/go/ssa"
"golang.org/x/tools/go/ssa/ssautil"
)
func main() {
cfg := packages.Config{Mode: packages.LoadAllSyntax}
pkgs, err := packages.Load(&cfg, "./hello-world")
if err != nil || packages.PrintErrors(pkgs) > 0 {
panic(err)
}
prog, ssaPkgs := ssautil.AllPackages(pkgs, 0)
prog.Build()
// At this point, ssaPkgs contains a complete representation of our program.
t := translator{
goToIR: map[ssa.Value]irvalue.Value{},
}
// Implement println in terms of printf, which is available to clang (libc).
t.printf = t.m.NewFunc("printf", irtypes.Void)
t.printf.Sig.Variadic = true
// Translate packages in turn.
for _, ssaPkg := range ssaPkgs {
t.translatePkg(ssaPkg)
}
// Write the LLVM assembly to stdout.
os.Stdout.WriteString(t.m.String())
}
type translator struct {
m ir.Module
printf *ir.Func
// goToIR keeps track of values (and outputs of instructions) we have
// previously translated.
goToIR map[ssa.Value]irvalue.Value
}
func (t *translator) translatePkg(ssaPkg *ssa.Package) {
// Loop over members of the package, plucking out functions.
for _, ssaM := range ssaPkg.Members {
switch ssaM := ssaM.(type) {
case *ssa.Function:
t.translateFunction(ssaM)
}
}
}
func (t *translator) translateFunction(goFn *ssa.Function) {
if goFn.Name() != "main" {
return // For simplicity, ignore all funcs other than main (init, etc).
}
// Loop over basic blocks.
irFn := t.m.NewFunc(goFn.Name(), irtypes.Void)
for _, goBlock := range goFn.Blocks {
irBlock := irFn.NewBlock(goBlock.String())
// Loop over instructions.
for _, goInst := range goBlock.Instrs {
t.translateInst(irBlock, goInst)
}
}
}
func (t *translator) translateValue(goValue ssa.Value) irvalue.Value {
irValue, ok := t.goToIR[goValue]
if ok {
// Values which have already been seen.
return irValue
}
switch goValue := goValue.(type) {
case *ssa.Const: // Constant translation.
switch goValue.Value.Kind() {
case goconstant.Int:
return irconstant.NewInt(irtypes.I64, goValue.Int64())
case goconstant.String:
strVal := goconstant.StringVal(goValue.Value)
return t.m.NewGlobalDef("$const_str", irconstant.NewCharArrayFromString(strVal))
default:
panic("unimplemented constant")
}
case *ssa.Builtin:
switch goValue.Name() {
case "println":
return t.printf
default:
panic("unimplemented builtin")
}
default:
panic(fmt.Errorf("unimplemented translateValue: %T: %v", goValue, goValue))
}
}
func (t *translator) translateInst(irBlock *ir.Block, goInst ssa.Instruction) {
// For now we just implement enough to translate our simple function. That
// means only calls, binary operations and returns are supported. Even that
// support is limited to what is needed to run the hello world program.
switch goInst := goInst.(type) {
case *ssa.Call:
// irCallee will be our builtin printf.
irCallee := t.translateValue(goInst.Call.Value)
var irArgs []irvalue.Value
for _, goArg := range goInst.Call.Args {
irArgs = append(irArgs, t.translateValue(goArg))
}
t.goToIR[goInst] = irBlock.NewCall(irCallee, irArgs...)
case *ssa.BinOp:
// Note: Here we implement 'Add', but of course there are many binary
// operators.
irX := t.translateValue(goInst.X)
irY := t.translateValue(goInst.Y)
t.goToIR[goInst] = irBlock.NewAdd(irX, irY)
case *ssa.Return:
irBlock.NewRet(nil) // TODO(pwaller): Implement returning values.
default:
panic("unimplemented instruction")
}
}