99 "go/token"
1010 "go/types"
1111 "path/filepath"
12+ "sort"
1213 "strconv"
1314 "strings"
1415
@@ -53,6 +54,45 @@ type compilerContext struct {
5354 astComments map [string ]* ast.CommentGroup
5455}
5556
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+
5696// builder contains all information relevant to build a single function.
5797type builder struct {
5898 * compilerContext
@@ -76,6 +116,18 @@ type builder struct {
76116 deferBuiltinFuncs map [ssa.Value ]deferBuiltin
77117}
78118
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+
79131type deferBuiltin struct {
80132 funcName string
81133 callback int
@@ -127,94 +179,47 @@ func NewTargetMachine(config *compileopts.Config) (llvm.TargetMachine, error) {
127179 return machine , nil
128180}
129181
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 ()
151188
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 {
167191 }
168192
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 {
180195 // 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 {
183198 // 64 bits target
184- c . intType = c . ctx . Int64Type ()
199+ intWidth = 64
185200 } else {
186201 panic ("unknown pointer size" )
187202 }
188- c .i8ptrType = llvm .PointerType (c .ctx .Int8Type (), 0 )
189203
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 )),
203208 }
209+ }
204210
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 )
211216
212217 c .ir = ir .NewProgram (lprogram )
213218
214219 // Run a simple dead code elimination pass.
215- err = c .ir .SimpleDCE ()
220+ err : = c .ir .SimpleDCE ()
216221 if err != nil {
217- return output , []error {err }
222+ return llvm. Module {} , []error {err }
218223 }
219224
220225 // Initialize debug information.
@@ -265,15 +270,7 @@ func Compile(pkgName string, machine llvm.TargetMachine, config *compileopts.Con
265270 }
266271
267272 // 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 )
277274 b .createFunctionDefinition ()
278275 }
279276
@@ -352,14 +349,64 @@ func Compile(pkgName string, machine llvm.TargetMachine, config *compileopts.Con
352349 c .dibuilder .Finalize ()
353350 }
354351
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+ })
359389 }
360390 }
361391
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
363410}
364411
365412// getLLVMRuntimeType obtains a named type from the runtime package and returns
0 commit comments