9
9
"go/token"
10
10
"go/types"
11
11
"path/filepath"
12
+ "sort"
12
13
"strconv"
13
14
"strings"
14
15
@@ -53,6 +54,45 @@ type compilerContext struct {
53
54
astComments map [string ]* ast.CommentGroup
54
55
}
55
56
57
+ // newCompilerContext returns a new compiler context ready for use, most
58
+ // importantly with a newly created LLVM context and module.
59
+ func newCompilerContext (moduleName string , machine llvm.TargetMachine , config * compileopts.Config ) * compilerContext {
60
+ c := & compilerContext {
61
+ Config : config ,
62
+ difiles : make (map [string ]llvm.Metadata ),
63
+ ditypes : make (map [types.Type ]llvm.Metadata ),
64
+ machine : machine ,
65
+ targetData : machine .CreateTargetData (),
66
+ }
67
+
68
+ c .ctx = llvm .NewContext ()
69
+ c .mod = c .ctx .NewModule (moduleName )
70
+ c .mod .SetTarget (config .Triple ())
71
+ c .mod .SetDataLayout (c .targetData .String ())
72
+ if c .Debug () {
73
+ c .dibuilder = llvm .NewDIBuilder (c .mod )
74
+ }
75
+
76
+ c .uintptrType = c .ctx .IntType (c .targetData .PointerSize () * 8 )
77
+ if c .targetData .PointerSize () <= 4 {
78
+ // 8, 16, 32 bits targets
79
+ c .intType = c .ctx .Int32Type ()
80
+ } else if c .targetData .PointerSize () == 8 {
81
+ // 64 bits target
82
+ c .intType = c .ctx .Int64Type ()
83
+ } else {
84
+ panic ("unknown pointer size" )
85
+ }
86
+ c .i8ptrType = llvm .PointerType (c .ctx .Int8Type (), 0 )
87
+
88
+ dummyFuncType := llvm .FunctionType (c .ctx .VoidType (), nil , false )
89
+ dummyFunc := llvm .AddFunction (c .mod , "tinygo.dummy" , dummyFuncType )
90
+ c .funcPtrAddrSpace = dummyFunc .Type ().PointerAddressSpace ()
91
+ dummyFunc .EraseFromParentAsFunction ()
92
+
93
+ return c
94
+ }
95
+
56
96
// builder contains all information relevant to build a single function.
57
97
type builder struct {
58
98
* compilerContext
@@ -76,6 +116,18 @@ type builder struct {
76
116
deferBuiltinFuncs map [ssa.Value ]deferBuiltin
77
117
}
78
118
119
+ func newBuilder (c * compilerContext , irbuilder llvm.Builder , f * ir.Function ) * builder {
120
+ return & builder {
121
+ compilerContext : c ,
122
+ Builder : irbuilder ,
123
+ fn : f ,
124
+ locals : make (map [ssa.Value ]llvm.Value ),
125
+ dilocals : make (map [* types.Var ]llvm.Metadata ),
126
+ blockEntries : make (map [* ssa.BasicBlock ]llvm.BasicBlock ),
127
+ blockExits : make (map [* ssa.BasicBlock ]llvm.BasicBlock ),
128
+ }
129
+ }
130
+
79
131
type deferBuiltin struct {
80
132
funcName string
81
133
callback int
@@ -127,94 +179,47 @@ func NewTargetMachine(config *compileopts.Config) (llvm.TargetMachine, error) {
127
179
return machine , nil
128
180
}
129
181
130
- // CompilerOutput is returned from the Compile() call. It contains the compile
131
- // output and information necessary to continue to compile and link the program.
132
- type CompilerOutput struct {
133
- // The LLVM module that contains the compiled but not optimized LLVM module
134
- // for all the Go code in the program.
135
- Mod llvm.Module
136
-
137
- // ExtraFiles is a list of C source files included in packages that should
138
- // be built and linked together with the main executable to form one
139
- // program. They can be used from CGo, for example.
140
- ExtraFiles []string
141
-
142
- // ExtraLDFlags are linker flags obtained during CGo processing. These flags
143
- // must be passed to the linker which links the entire executable.
144
- ExtraLDFlags []string
145
-
146
- // MainDir is the absolute directory path to the directory of the main
147
- // package. This is useful for testing: tests must be run in the package
148
- // directory that is being tested.
149
- MainDir string
150
- }
182
+ // Sizes returns a types.Sizes appropriate for the given target machine. It
183
+ // includes the correct int size and aligment as is necessary for the Go
184
+ // typechecker.
185
+ func Sizes (machine llvm.TargetMachine ) types.Sizes {
186
+ targetData := machine .CreateTargetData ()
187
+ defer targetData .Dispose ()
151
188
152
- // Compile the given package path or .go file path. Return an error when this
153
- // fails (in any stage). If successful it returns the LLVM module and a list of
154
- // extra C files to be compiled. If not, one or more errors will be returned.
155
- //
156
- // The fact that it returns a list of filenames to compile is a layering
157
- // violation. Eventually, this Compile function should only compile a single
158
- // package and not the whole program, and loading of the program (including CGo
159
- // processing) should be moved outside the compiler package.
160
- func Compile (pkgName string , machine llvm.TargetMachine , config * compileopts.Config ) (output CompilerOutput , errors []error ) {
161
- c := & compilerContext {
162
- Config : config ,
163
- difiles : make (map [string ]llvm.Metadata ),
164
- ditypes : make (map [types.Type ]llvm.Metadata ),
165
- machine : machine ,
166
- targetData : machine .CreateTargetData (),
189
+ intPtrType := targetData .IntPtrType ()
190
+ if intPtrType .IntTypeWidth ()/ 8 <= 32 {
167
191
}
168
192
169
- c .ctx = llvm .NewContext ()
170
- c .mod = c .ctx .NewModule (pkgName )
171
- c .mod .SetTarget (config .Triple ())
172
- c .mod .SetDataLayout (c .targetData .String ())
173
- if c .Debug () {
174
- c .dibuilder = llvm .NewDIBuilder (c .mod )
175
- }
176
- output .Mod = c .mod
177
-
178
- c .uintptrType = c .ctx .IntType (c .targetData .PointerSize () * 8 )
179
- if c .targetData .PointerSize () <= 4 {
193
+ var intWidth int
194
+ if targetData .PointerSize () <= 4 {
180
195
// 8, 16, 32 bits targets
181
- c . intType = c . ctx . Int32Type ()
182
- } else if c . targetData .PointerSize () == 8 {
196
+ intWidth = 32
197
+ } else if targetData .PointerSize () == 8 {
183
198
// 64 bits target
184
- c . intType = c . ctx . Int64Type ()
199
+ intWidth = 64
185
200
} else {
186
201
panic ("unknown pointer size" )
187
202
}
188
- c .i8ptrType = llvm .PointerType (c .ctx .Int8Type (), 0 )
189
203
190
- dummyFuncType := llvm .FunctionType (c .ctx .VoidType (), nil , false )
191
- dummyFunc := llvm .AddFunction (c .mod , "tinygo.dummy" , dummyFuncType )
192
- c .funcPtrAddrSpace = dummyFunc .Type ().PointerAddressSpace ()
193
- dummyFunc .EraseFromParentAsFunction ()
194
-
195
- lprogram , err := loader .Load (c .Config , []string {pkgName }, c .ClangHeaders , types.Config {
196
- Sizes : & stdSizes {
197
- IntSize : int64 (c .targetData .TypeAllocSize (c .intType )),
198
- PtrSize : int64 (c .targetData .PointerSize ()),
199
- MaxAlign : int64 (c .targetData .PrefTypeAlignment (c .i8ptrType )),
200
- }})
201
- if err != nil {
202
- return output , []error {err }
204
+ return & stdSizes {
205
+ IntSize : int64 (intWidth / 8 ),
206
+ PtrSize : int64 (targetData .PointerSize ()),
207
+ MaxAlign : int64 (targetData .PrefTypeAlignment (intPtrType )),
203
208
}
209
+ }
204
210
205
- err = lprogram .Parse ()
206
- if err != nil {
207
- return output , []error {err }
208
- }
209
- output .ExtraLDFlags = lprogram .LDFlags
210
- output .MainDir = lprogram .MainPkg ().Dir
211
+ // CompileProgram compiles the given package path or .go file path. Return an
212
+ // error when this fails (in any stage). If successful it returns the LLVM
213
+ // module. If not, one or more errors will be returned.
214
+ func CompileProgram (pkgName string , lprogram * loader.Program , machine llvm.TargetMachine , config * compileopts.Config ) (llvm.Module , []error ) {
215
+ c := newCompilerContext (pkgName , machine , config )
211
216
212
217
c .ir = ir .NewProgram (lprogram )
213
218
214
219
// Run a simple dead code elimination pass.
215
- err = c .ir .SimpleDCE ()
220
+ err : = c .ir .SimpleDCE ()
216
221
if err != nil {
217
- return output , []error {err }
222
+ return llvm. Module {} , []error {err }
218
223
}
219
224
220
225
// Initialize debug information.
@@ -265,15 +270,7 @@ func Compile(pkgName string, machine llvm.TargetMachine, config *compileopts.Con
265
270
}
266
271
267
272
// Create the function definition.
268
- b := builder {
269
- compilerContext : c ,
270
- Builder : irbuilder ,
271
- fn : f ,
272
- locals : make (map [ssa.Value ]llvm.Value ),
273
- dilocals : make (map [* types.Var ]llvm.Metadata ),
274
- blockEntries : make (map [* ssa.BasicBlock ]llvm.BasicBlock ),
275
- blockExits : make (map [* ssa.BasicBlock ]llvm.BasicBlock ),
276
- }
273
+ b := newBuilder (c , irbuilder , f )
277
274
b .createFunctionDefinition ()
278
275
}
279
276
@@ -352,14 +349,64 @@ func Compile(pkgName string, machine llvm.TargetMachine, config *compileopts.Con
352
349
c .dibuilder .Finalize ()
353
350
}
354
351
355
- // Gather the list of (C) file paths that should be included in the build.
356
- for _ , pkg := range c .ir .LoaderProgram .Sorted () {
357
- for _ , filename := range pkg .CFiles {
358
- output .ExtraFiles = append (output .ExtraFiles , filepath .Join (pkg .Dir , filename ))
352
+ return c .mod , c .diagnostics
353
+ }
354
+
355
+ // CompilePackage compiles a single package to a LLVM module.
356
+ func CompilePackage (moduleName string , pkg * loader.Package , machine llvm.TargetMachine , config * compileopts.Config ) (llvm.Module , []error ) {
357
+ c := newCompilerContext (moduleName , machine , config )
358
+
359
+ // Build SSA from AST.
360
+ ssaPkg := pkg .LoadSSA ()
361
+ ssaPkg .Build ()
362
+
363
+ // Sort by position, so that the order of the functions in the IR matches
364
+ // the order of functions in the source file. This is useful for testing,
365
+ // for example.
366
+ var members []string
367
+ for name := range ssaPkg .Members {
368
+ members = append (members , name )
369
+ }
370
+ sort .Slice (members , func (i , j int ) bool {
371
+ iPos := ssaPkg.Members [members [i ]].Pos ()
372
+ jPos := ssaPkg.Members [members [j ]].Pos ()
373
+ if i == j {
374
+ // Cannot sort by pos, so do it by name.
375
+ return members [i ] < members [j ]
376
+ }
377
+ return iPos < jPos
378
+ })
379
+
380
+ // Create *ir.Functions objects.
381
+ var functions []* ir.Function
382
+ for _ , name := range members {
383
+ member := ssaPkg .Members [name ]
384
+ switch member := member .(type ) {
385
+ case * ssa.Function :
386
+ functions = append (functions , & ir.Function {
387
+ Function : member ,
388
+ })
359
389
}
360
390
}
361
391
362
- return output , c .diagnostics
392
+ // Declare all functions.
393
+ for _ , fn := range functions {
394
+ c .createFunctionDeclaration (fn )
395
+ }
396
+
397
+ // Add definitions to declarations.
398
+ irbuilder := c .ctx .NewBuilder ()
399
+ defer irbuilder .Dispose ()
400
+ for _ , f := range functions {
401
+ if f .Blocks == nil {
402
+ continue // external function
403
+ }
404
+ // Create the function definition.
405
+ b := newBuilder (c , irbuilder , f )
406
+ b .createFunctionDefinition ()
407
+ }
408
+
409
+ return c .mod , nil
363
410
}
364
411
365
412
// getLLVMRuntimeType obtains a named type from the runtime package and returns
0 commit comments